Hệ thống quản lý phòng khám trực tuyến bằng PHP

1 <?php
2 /**
3  * PHPMailer - PHP email creation and transport
class.
4  * PHP Version
5
5  * @package PHPMailer
6  * @link https://github.com/PHPMailer/PHPMailer/ The PHPMailer GitHub project
7  * @author Marcus Bointon (Synchro/coolbru) <phpmailer@synchromedia.co.uk>
8  * @author Jim Jagielski (jimjag) <jimjag@gmail.com>
9  * @author Andy Prevost (codeworxtech) <codeworxtech@users.sourceforge.net>
10  * @author Brent R. Matzelle (original founder)
11  * @copyright
2012 - 2014 Marcus Bointon
12  * @copyright
2010 - 2012 Jim Jagielski
13  * @copyright
2004 - 2009 Andy Prevost
14  * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
15  * @note This program
is distributed in the hope that it will be useful - WITHOUT
16  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
17  * FITNESS FOR A PARTICULAR PURPOSE.
18  */
19
20 /**
21  * PHPMailer - PHP email creation and transport
class.
22  * @package PHPMailer
23  * @author Marcus Bointon (Synchro/coolbru) <phpmailer@synchromedia.co.uk>
24  * @author Jim Jagielski (jimjag) <jimjag@gmail.com>
25  * @author Andy Prevost (codeworxtech) <codeworxtech@users.sourceforge.net>
26  * @author Brent R. Matzelle (original founder)
27  */

28 class
PHPMailer
29 {
30     
/**
31      * The PHPMailer Version number.
32      * @
var string
33      */

34     
public $Version = '5.2.23';
35
36     
/**
37      * Email priority.
38      * Options:
null (default), 1 = High, 3 = Normal, 5 = low.
39      * When
null, the header is not set at all.
40      * @
var integer
41      */

42     
public $Priority = null;
43
44     
/**
45      * The character
set of the message.
46      * @
var string
47      */

48     
public $CharSet = 'iso-8859-1';
49
50     
/**
51      * The MIME Content-type of the message.
52      * @
var string
53      */

54     
public $ContentType = 'text/plain';
55
56     
/**
57      * The message encoding.
58      * Options:
"8bit", "7bit", "binary", "base64", and "quoted-printable".
59      * @
var string
60      */

61     
public $Encoding = '8bit';
62
63     
/**
64      * Holds the most recent mailer error message.
65      * @
var string
66      */

67     
public $ErrorInfo = '';
68
69     
/**
70      * The From email address
for the message.
71      * @
var string
72      */

73     
public $From = 'root@localhost';
74
75     
/**
76      * The From name of the message.
77      * @
var string
78      */

79     
public $FromName = 'Root User';
80
81     
/**
82      * The Sender email (Return-Path) of the message.
83      * If not empty, will be sent via -f to sendmail or
as 'MAIL FROM' in smtp mode.
84      * @
var string
85      */

86     
public $Sender = '';
87
88     
/**
89      * The Return-Path of the message.
90      * If empty, it will be
set to either From or Sender.
91      * @
var string
92      * @deprecated Email senders should never
set a return-path header;
93      * it
's the receiver's job (RFC5321 section 4.4), so this no longer does anything.
94      * @link https://tools.ietf.org/html/rfc5321#section-
4.4 RFC5321 reference
95      */

96     
public $ReturnPath = '';
97
98     
/**
99      * The Subject of the message.
100      * @
var string
101      */

102     
public $Subject = '';
103
104     
/**
105      * An HTML or plain text message body.
106      * If HTML then call isHTML(
true).
107      * @
var string
108      */

109     
public $Body = '';
110
111     
/**
112      * The plain-text message body.
113      * This body can be read
by mail clients that do not have HTML email
114      * capability such
as mutt & Eudora.
115      * Clients that can read HTML will view the normal Body.
116      * @
var string
117      */

118     
public $AltBody = '';
119
120     
/**
121      * An iCal message part body.
122      * Only supported
in simple alt or alt_inline message types
123      * To generate iCal events, use the bundled extras/EasyPeasyICS.php
class or iCalcreator
124      * @link http://sprain.ch/blog/downloads/php-
class-easypeasyics-create-ical-files-with-php/
125      * @link http://kigkonsult.se/iCalcreator/
126      * @
var string
127      */

128     
public $Ical = '';
129
130     
/**
131      * The complete compiled MIME message body.
132      * @access
protected
133      * @
var string
134      */

135     
protected $MIMEBody = '';
136
137     
/**
138      * The complete compiled MIME message headers.
139      * @
var string
140      * @access
protected
141      */

142     
protected $MIMEHeader = '';
143
144     
/**
145      * Extra headers that createHeader() doesn
't fold in.
146      * @
var string
147      * @access
protected
148      */

149     
protected $mailHeader = '';
150
151     
/**
152      * Word-wrap the message body to
this number of chars.
153      * Set to
0 to not wrap. A useful value here is 78, for RFC2822 section 2.1.1 compliance.
154      * @
var integer
155      */

156     
public $WordWrap = 0;
157
158     
/**
159      * Which method to use to send mail.
160      * Options:
"mail", "sendmail", or "smtp".
161      * @
var string
162      */

163     
public $Mailer = 'mail';
164
165     
/**
166      * The path to the sendmail program.
167      * @
var string
168      */

169     
public $Sendmail = '/usr/sbin/sendmail';
170
171     
/**
172      * Whether mail() uses a fully sendmail-compatible MTA.
173      * One which supports sendmail
's "-oi -f" options.
174      * @
var boolean
175      */

176     
public $UseSendmailOptions = true;
177
178     
/**
179      * Path to PHPMailer plugins.
180      * Useful
if the SMTP class is not in the PHP include path.
181      * @
var string
182      * @deprecated Should not be needed now there
is an autoloader.
183      */

184     
public $PluginDir = '';
185
186     
/**
187      * The email address that a reading confirmation should be sent to, also known
as read receipt.
188      * @
var string
189      */

190     
public $ConfirmReadingTo = '';
191
192     
/**
193      * The hostname to use
in the Message-ID header and as default HELO string.
194      * If empty, PHPMailer attempts to find one with,
in order,
195      * $_SERVER[
'SERVER_NAME'], gethostname(), php_uname('n'), or the value
196      *
'localhost.localdomain'.
197      * @
var string
198      */

199     
public $Hostname = '';
200
201     
/**
202      * An ID to be used
in the Message-ID header.
203      * If empty, a unique id will be generated.
204      * You can
set your own, but it must be in the format "<id@domain>",
205      *
as defined in RFC5322 section 3.6.4 or it will be ignored.
206      * @see https://tools.ietf.org/html/rfc5322#section-
3.6.4
207      * @
var string
208      */

209     
public $MessageID = '';
210
211     
/**
212      * The message Date to be used
in the Date header.
213      * If empty, the current date will be added.
214      * @
var string
215      */

216     
public $MessageDate = '';
217
218     
/**
219      * SMTP hosts.
220      * Either a single hostname or multiple semicolon-delimited hostnames.
221      * You can also specify a different port
222      *
for each host by using this format: [hostname:port]
223      * (e.g.
"smtp1.example.com:25;smtp2.example.com").
224      * You can also specify encryption type,
for example:
225      * (e.g.
"tls://smtp1.example.com:587;ssl://smtp2.example.com:465").
226      * Hosts will be tried
in order.
227      * @
var string
228      */

229     
public $Host = 'localhost';
230
231     
/**
232      * The
default SMTP server port.
233      * @
var integer
234      * @TODO Why
is this needed when the SMTP class takes care of it?
235      */

236     
public $Port = 25;
237
238     
/**
239      * The SMTP HELO of the message.
240      * Default
is $Hostname. If $Hostname is empty, PHPMailer attempts to find
241      * one with the same method described above
for $Hostname.
242      * @
var string
243      * @see PHPMailer::$Hostname
244      */

245     
public $Helo = '';
246
247     
/**
248      * What kind of encryption to use
on the SMTP connection.
249      * Options:
'', 'ssl' or 'tls'
250      * @
var string
251      */

252     
public $SMTPSecure = '';
253
254     
/**
255      * Whether to enable TLS encryption automatically
if a server supports it,
256      * even
if `SMTPSecure` is not set to 'tls'.
257      * Be aware that
in PHP >= 5.6 this requires that the server's certificates are valid.
258      * @
var boolean
259      */

260     
public $SMTPAutoTLS = true;
261
262     
/**
263      * Whether to use SMTP authentication.
264      * Uses the Username and Password properties.
265      * @
var boolean
266      * @see PHPMailer::$Username
267      * @see PHPMailer::$Password
268      */

269     
public $SMTPAuth = false;
270
271     
/**
272      * Options array passed to stream_context_create
when connecting via SMTP.
273      * @
var array
274      */

275     
public $SMTPOptions = array();
276
277     
/**
278      * SMTP username.
279      * @
var string
280      */

281     
public $Username = '';
282
283     
/**
284      * SMTP password.
285      * @
var string
286      */

287     
public $Password = '';
288
289     
/**
290      * SMTP auth type.
291      * Options are CRAM-MD5, LOGIN, PLAIN, NTLM, XOAUTH2, attempted
in that order if not specified
292      * @
var string
293      */

294     
public $AuthType = '';
295
296     
/**
297      * SMTP realm.
298      * Used
for NTLM auth
299      * @
var string
300      */

301     
public $Realm = '';
302
303     
/**
304      * SMTP workstation.
305      * Used
for NTLM auth
306      * @
var string
307      */

308     
public $Workstation = '';
309
310     
/**
311      * The SMTP server timeout
in seconds.
312      * Default of
5 minutes (300sec) is from RFC2821 section 4.5.3.2
313      * @
var integer
314      */

315     
public $Timeout = 300;
316
317     
/**
318      * SMTP
class debug output mode.
319      * Debug output level.
320      * Options:
321      * * `
0` No output
322      * * `
1` Commands
323      * * `
2` Data and commands
324      * * `
3` As 2 plus connection status
325      * * `
4` Low-level data output
326      * @
var integer
327      * @see SMTP::$do_debug
328      */

329     
public $SMTPDebug = 0;
330
331     
/**
332      * How to handle debug output.
333      * Options:
334      * * `echo` Output plain-text
as-is, appropriate for CLI
335      * * `html` Output escaped, line breaks converted to `<br>`, appropriate
for browser output
336      * * `error_log` Output to error log
as configured in php.ini
337      *
338      * Alternatively, you can provide a callable expecting two
params: a message string and the debug level:
339      * <code>
340      * $mail->Debugoutput = function($str, $level) {echo
"debug level $level; message: $str";};
341      * </code>
342      * @
var string|callable
343      * @see SMTP::$Debugoutput
344      */

345     
public $Debugoutput = 'echo';
346
347     
/**
348      * Whether to keep SMTP connection open after each message.
349      * If
this is set to true then to close the connection
350      * requires an
explicit call to smtpClose().
351      * @
var boolean
352      */

353     
public $SMTPKeepAlive = false;
354
355     
/**
356      * Whether to split multiple to addresses
into multiple messages
357      * or send them all
in one message.
358      * Only supported
in `mail` and `sendmail` transports, not in SMTP.
359      * @
var boolean
360      */

361     
public $SingleTo = false;
362
363     
/**
364      * Storage
for addresses when SingleTo is enabled.
365      * @
var array
366      * @TODO This should really not be
public
367      */

368     
public $SingleToArray = array();
369
370     
/**
371      * Whether to generate VERP addresses
on send.
372      * Only applicable
when sending via SMTP.
373      * @link https://en.wikipedia.org/wiki/Variable_envelope_return_path
374      * @link http://www.postfix.org/VERP_README.html Postfix VERP info
375      * @
var boolean
376      */

377     
public $do_verp = false;
378
379     
/**
380      * Whether to allow sending messages with an empty body.
381      * @
var boolean
382      */

383     
public $AllowEmpty = false;
384
385     
/**
386      * The
default line ending.
387      * @note The
default remains "\n". We force CRLF where we know
388      * it must be used via self::CRLF.
389      * @
var string
390      */

391     
public $LE = "\n";
392
393     
/**
394      * DKIM selector.
395      * @
var string
396      */

397     
public $DKIM_selector = '';
398
399     
/**
400      * DKIM Identity.
401      * Usually the email address used
as the source of the email.
402      * @
var string
403      */

404     
public $DKIM_identity = '';
405
406     
/**
407      * DKIM passphrase.
408      * Used
if your key is encrypted.
409      * @
var string
410      */

411     
public $DKIM_passphrase = '';
412
413     
/**
414      * DKIM signing domain name.
415      * @example
'example.com'
416      * @
var string
417      */

418     
public $DKIM_domain = '';
419
420     
/**
421      * DKIM
private key file path.
422      * @
var string
423      */

424     
public $DKIM_private = '';
425
426     
/**
427      * DKIM
private key string.
428      * If
set, takes precedence over `$DKIM_private`.
429      * @
var string
430      */

431     
public $DKIM_private_string = '';
432
433     
/**
434      * Callback Action function name.
435      *
436      * The function that handles the result of the send email action.
437      * It
is called out by send() for each email sent.
438      *
439      * Value can be any php callable: http://www.php.net/is_callable
440      *
441      * Parameters:
442      * boolean $result result of the send action
443      *
string $to email address of the recipient
444      *
string $cc cc email addresses
445      *
string $bcc bcc email addresses
446      *
string $subject the subject
447      *
string $body the email body
448      *
string $from email address of sender
449      * @
var string
450      */

451     
public $action_function = '';
452
453     
/**
454      * What to put
in the X-Mailer header.
455      * Options: An empty
string for PHPMailer default, whitespace for none, or a string to use
456      * @
var string
457      */

458     
public $XMailer = '';
459
460     
/**
461      * Which validator to use
by default when validating email addresses.
462      * May be a callable to inject your own validator, but there are several built-
in validators.
463      * @see PHPMailer::validateAddress()
464      * @
var string|callable
465      * @
static
466      */

467     
public static $validator = 'auto';
468
469     
/**
470      * An instance of the SMTP sender
class.
471      * @
var SMTP
472      * @access
protected
473      */

474     
protected $smtp = null;
475
476     
/**
477      * The array of
'to' names and addresses.
478      * @
var array
479      * @access
protected
480      */

481     
protected $to = array();
482
483     
/**
484      * The array of
'cc' names and addresses.
485      * @
var array
486      * @access
protected
487      */

488     
protected $cc = array();
489
490     
/**
491      * The array of
'bcc' names and addresses.
492      * @
var array
493      * @access
protected
494      */

495     
protected $bcc = array();
496
497     
/**
498      * The array of reply-to names and addresses.
499      * @
var array
500      * @access
protected
501      */

502     
protected $ReplyTo = array();
503
504     
/**
505      * An array of all kinds of addresses.
506      * Includes all of $to, $cc, $bcc
507      * @
var array
508      * @access
protected
509      * @see PHPMailer::$to @see PHPMailer::$cc @see PHPMailer::$bcc
510      */

511     
protected $all_recipients = array();
512
513     
/**
514      * An array of names and addresses queued
for validation.
515      * In send(), valid and non duplicate entries are moved to $all_recipients
516      * and one of $to, $cc, or $bcc.
517      * This array
is used only for addresses with IDN.
518      * @
var array
519      * @access
protected
520      * @see PHPMailer::$to @see PHPMailer::$cc @see PHPMailer::$bcc
521      * @see PHPMailer::$all_recipients
522      */

523     
protected $RecipientsQueue = array();
524
525     
/**
526      * An array of reply-to names and addresses queued
for validation.
527      * In send(), valid and non duplicate entries are moved to $ReplyTo.
528      * This array
is used only for addresses with IDN.
529      * @
var array
530      * @access
protected
531      * @see PHPMailer::$ReplyTo
532      */

533     
protected $ReplyToQueue = array();
534
535     
/**
536      * The array of attachments.
537      * @
var array
538      * @access
protected
539      */

540     
protected $attachment = array();
541
542     
/**
543      * The array of custom headers.
544      * @
var array
545      * @access
protected
546      */

547     
protected $CustomHeader = array();
548
549     
/**
550      * The most recent Message-ID (including angular brackets).
551      * @
var string
552      * @access
protected
553      */

554     
protected $lastMessageID = '';
555
556     
/**
557      * The message
's MIME type.
558      * @
var string
559      * @access
protected
560      */

561     
protected $message_type = '';
562
563     
/**
564      * The array of MIME boundary strings.
565      * @
var array
566      * @access
protected
567      */

568     
protected $boundary = array();
569
570     
/**
571      * The array of available languages.
572      * @
var array
573      * @access
protected
574      */

575     
protected $language = array();
576
577     
/**
578      * The number of errors encountered.
579      * @
var integer
580      * @access
protected
581      */

582     
protected $error_count = 0;
583
584     
/**
585      * The S/MIME certificate file path.
586      * @
var string
587      * @access
protected
588      */

589     
protected $sign_cert_file = '';
590
591     
/**
592      * The S/MIME key file path.
593      * @
var string
594      * @access
protected
595      */

596     
protected $sign_key_file = '';
597
598     
/**
599      * The optional S/MIME extra certificates (
"CA Chain") file path.
600      * @
var string
601      * @access
protected
602      */

603     
protected $sign_extracerts_file = '';
604
605     
/**
606      * The S/MIME password
for the key.
607      * Used only
if the key is encrypted.
608      * @
var string
609      * @access
protected
610      */

611     
protected $sign_key_pass = '';
612
613     
/**
614      * Whether to
throw exceptions for errors.
615      * @
var boolean
616      * @access
protected
617      */

618     
protected $exceptions = false;
619
620     
/**
621      * Unique ID used
for message ID and boundaries.
622      * @
var string
623      * @access
protected
624      */

625     
protected $uniqueid = '';
626
627     
/**
628      * Error severity: message only,
continue processing.
629      */

630     
const STOP_MESSAGE = 0;
631
632     
/**
633      * Error severity: message, likely ok to
continue processing.
634      */

635     
const STOP_CONTINUE = 1;
636
637     
/**
638      * Error severity: message, plus full stop, critical error reached.
639      */

640     
const STOP_CRITICAL = 2;
641
642     
/**
643      * SMTP RFC standard line ending.
644      */

645     
const CRLF = "\r\n";
646
647     
/**
648      * The maximum line length allowed
by RFC 2822 section 2.1.1
649      * @
var integer
650      */

651     
const MAX_LINE_LENGTH = 998;
652
653     
/**
654      * Constructor.
655      * @param boolean $exceptions Should we
throw external exceptions?
656      */

657     
public function __construct($exceptions = null)
658     {
659         
if ($exceptions !== null) {
660             $
this->exceptions = (boolean)$exceptions;
661         }
662     }
663
664     
/**
665      * Destructor.
666      */

667     
public function __destruct()
668     {
669         
//Close any open SMTP connection nicely
670         $
this->smtpClose();
671     }
672
673     
/**
674      * Call mail()
in a safe_mode-aware fashion.
675      * Also, unless sendmail_path points to sendmail (or something that
676      * claims to be sendmail), don
't pass params (not a perfect fix,
677      * but it will
do)
678      * @param
string $to To
679      * @param
string $subject Subject
680      * @param
string $body Message Body
681      * @param
string $header Additional Header(s)
682      * @param
string $params Params
683      * @access
private
684      * @
return boolean
685      */

686     
private function mailPassthru($to, $subject, $body, $header, $params)
687     {
688         
//Check overloading of mail function to avoid double-encoding
689         
if (ini_get('mbstring.func_overload') & 1) {
690             $subject = $
this->secureHeader($subject);
691         }
else {
692             $subject = $
this->encodeHeader($this->secureHeader($subject));
693         }
694
695         
//Can't use additional_parameters in safe_mode, calling mail() with null params breaks
696         
//@link http://php.net/manual/en/function.mail.php
697         
if (ini_get('safe_mode') or !$this->UseSendmailOptions or is_null($params)) {
698             $result = @mail($to, $subject, $body, $header);
699         }
else {
700             $result = @mail($to, $subject, $body, $header, $
params);
701         }
702         
return $result;
703     }
704     
/**
705      * Output debugging info via user-defined method.
706      * Only generates output
if SMTP debug output is enabled (@see SMTP::$do_debug).
707      * @see PHPMailer::$Debugoutput
708      * @see PHPMailer::$SMTPDebug
709      * @param
string $str
710      */

711     
protected function edebug($str)
712     {
713         
if ($this->SMTPDebug <= 0) {
714             
return;
715         }
716         
//Avoid clash with built-in function names
717         
if (!in_array($this->Debugoutput, array('error_log', 'html', 'echo')) and is_callable($this->Debugoutput)) {
718             call_user_func($
this->Debugoutput, $str, $this->SMTPDebug);
719             
return;
720         }
721         
switch ($this->Debugoutput) {
722             
case 'error_log':
723                 
//Don't output, just log
724                 error_log($str);
725                 
break;
726             
case 'html':
727                 
//Cleans up output a bit for a better looking, HTML-safe output
728                 echo htmlentities(
729                     preg_replace(
'/[\r\n]+/', '', $str),
730                     ENT_QUOTES,
731                     
'UTF-8'
732                 )
733                 .
"<br>\n";
734                 
break;
735             
case 'echo':
736             
default:
737                 
//Normalize line breaks
738                 $str = preg_replace(
'/\r\n?/ms', "\n", $str);
739                 echo gmdate(
'Y-m-d H:i:s') . "\t" . str_replace(
740                     
"\n",
741                     
"\n \t ",
742                     trim($str)
743                 ) .
"\n";
744         }
745     }
746
747     
/**
748      * Sets message type to HTML or plain.
749      * @param boolean $isHtml True
for HTML mode.
750      * @
return void
751      */

752     
public function isHTML($isHtml = true)
753     {
754         
if ($isHtml) {
755             $
this->ContentType = 'text/html';
756         }
else {
757             $
this->ContentType = 'text/plain';
758         }
759     }
760
761     
/**
762      * Send messages
using SMTP.
763      * @
return void
764      */

765     
public function isSMTP()
766     {
767         $
this->Mailer = 'smtp';
768     }
769
770     
/**
771      * Send messages
using PHP's mail() function.
772      * @
return void
773      */

774     
public function isMail()
775     {
776         $
this->Mailer = 'mail';
777     }
778
779     
/**
780      * Send messages
using $Sendmail.
781      * @
return void
782      */

783     
public function isSendmail()
784     {
785         $ini_sendmail_path = ini_get(
'sendmail_path');
786
787         
if (!stristr($ini_sendmail_path, 'sendmail')) {
788             $
this->Sendmail = '/usr/sbin/sendmail';
789         }
else {
790             $
this->Sendmail = $ini_sendmail_path;
791         }
792         $
this->Mailer = 'sendmail';
793     }
794
795     
/**
796      * Send messages
using qmail.
797      * @
return void
798      */

799     
public function isQmail()
800     {
801         $ini_sendmail_path = ini_get(
'sendmail_path');
802
803         
if (!stristr($ini_sendmail_path, 'qmail')) {
804             $
this->Sendmail = '/var/qmail/bin/qmail-inject';
805         }
else {
806             $
this->Sendmail = $ini_sendmail_path;
807         }
808         $
this->Mailer = 'qmail';
809     }
810
811     
/**
812      * Add a
"To" address.
813      * @param
string $address The email address to send to
814      * @param
string $name
815      * @
return boolean true on success, false if address already used or invalid in some way
816      */

817     
public function addAddress($address, $name = '')
818     {
819         
return $this->addOrEnqueueAnAddress('to', $address, $name);
820     }
821
822     
/**
823      * Add a
"CC" address.
824      * @note: This function works with the SMTP mailer
on win32, not with the "mail" mailer.
825      * @param
string $address The email address to send to
826      * @param
string $name
827      * @
return boolean true on success, false if address already used or invalid in some way
828      */

829     
public function addCC($address, $name = '')
830     {
831         
return $this->addOrEnqueueAnAddress('cc', $address, $name);
832     }
833
834     
/**
835      * Add a
"BCC" address.
836      * @note: This function works with the SMTP mailer
on win32, not with the "mail" mailer.
837      * @param
string $address The email address to send to
838      * @param
string $name
839      * @
return boolean true on success, false if address already used or invalid in some way
840      */

841     
public function addBCC($address, $name = '')
842     {
843         
return $this->addOrEnqueueAnAddress('bcc', $address, $name);
844     }
845
846     
/**
847      * Add a
"Reply-To" address.
848      * @param
string $address The email address to reply to
849      * @param
string $name
850      * @
return boolean true on success, false if address already used or invalid in some way
851      */

852     
public function addReplyTo($address, $name = '')
853     {
854         
return $this->addOrEnqueueAnAddress('Reply-To', $address, $name);
855     }
856
857     
/**
858      * Add an address to one of the recipient arrays or to the ReplyTo array. Because PHPMailer
859      * can
't validate addresses with an IDN without knowing the PHPMailer::$CharSet (that can still
860      * be modified after calling
this function), addition of such addresses is delayed until send().
861      * Addresses that have been added already
return false, but do not throw exceptions.
862      * @param
string $kind One of 'to', 'cc', 'bcc', or 'ReplyTo'
863      * @param
string $address The email address to send, resp. to reply to
864      * @param
string $name
865      * @throws phpmailerException
866      * @
return boolean true on success, false if address already used or invalid in some way
867      * @access
protected
868      */

869     
protected function addOrEnqueueAnAddress($kind, $address, $name)
870     {
871         $address = trim($address);
872         $name = trim(preg_replace(
'/[\r\n]+/', '', $name)); //Strip breaks and trim
873         
if (($pos = strrpos($address, '@')) === false) {
874             
// At-sign is misssing.
875             $error_message = $
this->lang('invalid_address') . " (addAnAddress $kind): $address";
876             $
this->setError($error_message);
877             $
this->edebug($error_message);
878             
if ($this->exceptions) {
879                 
throw new phpmailerException($error_message);
880             }
881             
return false;
882         }
883         $
params = array($kind, $address, $name);
884         
// Enqueue addresses with IDN until we know the PHPMailer::$CharSet.
885         
if ($this->has8bitChars(substr($address, ++$pos)) and $this->idnSupported()) {
886             
if ($kind != 'Reply-To') {
887                 
if (!array_key_exists($address, $this->RecipientsQueue)) {
888                     $
this->RecipientsQueue[$address] = $params;
889                     
return true;
890                 }
891             }
else {
892                 
if (!array_key_exists($address, $this->ReplyToQueue)) {
893                     $
this->ReplyToQueue[$address] = $params;
894                     
return true;
895                 }
896             }
897             
return false;
898         }
899         
// Immediately add standard addresses without IDN.
900         
return call_user_func_array(array($this, 'addAnAddress'), $params);
901     }
902
903     
/**
904      * Add an address to one of the recipient arrays or to the ReplyTo array.
905      * Addresses that have been added already
return false, but do not throw exceptions.
906      * @param
string $kind One of 'to', 'cc', 'bcc', or 'ReplyTo'
907      * @param
string $address The email address to send, resp. to reply to
908      * @param
string $name
909      * @throws phpmailerException
910      * @
return boolean true on success, false if address already used or invalid in some way
911      * @access
protected
912      */

913     
protected function addAnAddress($kind, $address, $name = '')
914     {
915         
if (!in_array($kind, array('to', 'cc', 'bcc', 'Reply-To'))) {
916             $error_message = $
this->lang('Invalid recipient kind: ') . $kind;
917             $
this->setError($error_message);
918             $
this->edebug($error_message);
919             
if ($this->exceptions) {
920                 
throw new phpmailerException($error_message);
921             }
922             
return false;
923         }
924         
if (!$this->validateAddress($address)) {
925             $error_message = $
this->lang('invalid_address') . " (addAnAddress $kind): $address";
926             $
this->setError($error_message);
927             $
this->edebug($error_message);
928             
if ($this->exceptions) {
929                 
throw new phpmailerException($error_message);
930             }
931             
return false;
932         }
933         
if ($kind != 'Reply-To') {
934             
if (!array_key_exists(strtolower($address), $this->all_recipients)) {
935                 array_push($
this->$kind, array($address, $name));
936                 $
this->all_recipients[strtolower($address)] = true;
937                 
return true;
938             }
939         }
else {
940             
if (!array_key_exists(strtolower($address), $this->ReplyTo)) {
941                 $
this->ReplyTo[strtolower($address)] = array($address, $name);
942                 
return true;
943             }
944         }
945         
return false;
946     }
947
948     
/**
949      * Parse and validate a
string containing one or more RFC822-style comma-separated email addresses
950      * of the form
"display name <address>" into an array of name/address pairs.
951      * Uses the imap_rfc822_parse_adrlist function
if the IMAP extension is available.
952      * Note that quotes
in the name part are removed.
953      * @param
string $addrstr The address list string
954      * @param
bool $useimap Whether to use the IMAP extension to parse the list
955      * @
return array
956      * @link http://www.andrew.cmu.edu/user/agreen1/testing/mrbs/web/Mail/RFC822.php A more careful implementation
957      */

958     
public function parseAddresses($addrstr, $useimap = true)
959     {
960         $addresses = array();
961         
if ($useimap and function_exists('imap_rfc822_parse_adrlist')) {
962             
//Use this built-in parser if it's available
963             $list = imap_rfc822_parse_adrlist($addrstr,
'');
964             
foreach ($list as $address) {
965                 
if ($address->host != '.SYNTAX-ERROR.') {
966                     
if ($this->validateAddress($address->mailbox . '@' . $address->host)) {
967                         $addresses[] = array(
968                             
'name' => (property_exists($address, 'personal') ? $address->personal : ''),
969                             
'address' => $address->mailbox . '@' . $address->host
970                         );
971                     }
972                 }
973             }
974         }
else {
975             
//Use this simpler parser
976             $list = explode(
',', $addrstr);
977             
foreach ($list as $address) {
978                 $address = trim($address);
979                 
//Is there a separate name part?
980                 
if (strpos($address, '<') === false) {
981                     
//No separate name, just use the whole thing
982                     
if ($this->validateAddress($address)) {
983                         $addresses[] = array(
984                             
'name' => '',
985                             
'address' => $address
986                         );
987                     }
988                 }
else {
989                     list($name, $email) = explode(
'<', $address);
990                     $email = trim(str_replace(
'>', '', $email));
991                     
if ($this->validateAddress($email)) {
992                         $addresses[] = array(
993                             
'name' => trim(str_replace(array('"', "'"), '', $name)),
994                             
'address' => $email
995                         );
996                     }
997                 }
998             }
999         }
1000         
return $addresses;
1001     }
1002
1003     
/**
1004      * Set the From and FromName properties.
1005      * @param
string $address
1006      * @param
string $name
1007      * @param boolean $auto Whether to also
set the Sender address, defaults to true
1008      * @throws phpmailerException
1009      * @
return boolean
1010      */

1011     
public function setFrom($address, $name = '', $auto = true)
1012     {
1013         $address = trim($address);
1014         $name = trim(preg_replace(
'/[\r\n]+/', '', $name)); //Strip breaks and trim
1015         
// Don't validate now addresses with IDN. Will be done in send().
1016         
if (($pos = strrpos($address, '@')) === false or
1017             (!$
this->has8bitChars(substr($address, ++$pos)) or !$this->idnSupported()) and
1018             !$
this->validateAddress($address)) {
1019             $error_message = $
this->lang('invalid_address') . " (setFrom) $address";
1020             $
this->setError($error_message);
1021             $
this->edebug($error_message);
1022             
if ($this->exceptions) {
1023                 
throw new phpmailerException($error_message);
1024             }
1025             
return false;
1026         }
1027         $
this->From = $address;
1028         $
this->FromName = $name;
1029         
if ($auto) {
1030             
if (empty($this->Sender)) {
1031                 $
this->Sender = $address;
1032             }
1033         }
1034         
return true;
1035     }
1036
1037     
/**
1038      * Return the Message-ID header of the last email.
1039      * Technically
this is the value from the last time the headers were created,
1040      * but it
's also the message ID of the last sent message except in
1041      * pathological cases.
1042      * @
return string
1043      */

1044     
public function getLastMessageID()
1045     {
1046         
return $this->lastMessageID;
1047     }
1048
1049     
/**
1050      * Check that a
string looks like an email address.
1051      * @param
string $address The email address to check
1052      * @param
string|callable $patternselect A selector for the validation pattern to use :
1053      * * `auto` Pick best pattern automatically;
1054      * * `pcre8` Use the squiloople.com pattern, requires PCRE >
8.0, PHP >= 5.3.2, 5.2.14;
1055      * * `pcre` Use old PCRE implementation;
1056      * * `php` Use PHP built-
in FILTER_VALIDATE_EMAIL;
1057      * * `html5` Use the pattern given
by the HTML5 spec for 'email' type form input elements.
1058      * * `noregex` Don
't use a regex: super fast, really dumb.
1059      * Alternatively you may pass
in a callable to inject your own validator, for example:
1060      * PHPMailer::validateAddress(
'user@example.com', function($address) {
1061      *
return (strpos($address, '@') !== false);
1062      * });
1063      * You can also
set the PHPMailer::$validator static to a callable, allowing built-in methods to use your validator.
1064      * @
return boolean
1065      * @
static
1066      * @access
public
1067      */

1068     
public static function validateAddress($address, $patternselect = null)
1069     {
1070         
if (is_null($patternselect)) {
1071             $patternselect = self::$validator;
1072         }
1073         
if (is_callable($patternselect)) {
1074             
return call_user_func($patternselect, $address);
1075         }
1076         
//Reject line breaks in addresses; it's valid RFC5322, but not RFC5321
1077         
if (strpos($address, "\n") !== false or strpos($address, "\r") !== false) {
1078             
return false;
1079         }
1080         
if (!$patternselect or $patternselect == 'auto') {
1081             
//Check this constant first so it works when extension_loaded() is disabled by safe mode
1082             
//Constant was added in PHP 5.2.4
1083             
if (defined('PCRE_VERSION')) {
1084                 
//This pattern can get stuck in a recursive loop in PCRE <= 8.0.2
1085                 
if (version_compare(PCRE_VERSION, '8.0.3') >= 0) {
1086                     $patternselect =
'pcre8';
1087                 }
else {
1088                     $patternselect =
'pcre';
1089                 }
1090             } elseif (function_exists(
'extension_loaded') and extension_loaded('pcre')) {
1091                 
//Fall back to older PCRE
1092                 $patternselect =
'pcre';
1093             }
else {
1094                 
//Filter_var appeared in PHP 5.2.0 and does not require the PCRE extension
1095                 
if (version_compare(PHP_VERSION, '5.2.0') >= 0) {
1096                     $patternselect =
'php';
1097                 }
else {
1098                     $patternselect =
'noregex';
1099                 }
1100             }
1101         }
1102         
switch ($patternselect) {
1103             
case 'pcre8':
1104                 
/**
1105                  * Uses the same RFC5322 regex
on which FILTER_VALIDATE_EMAIL is based, but allows dotless domains.
1106                  * @link http://squiloople.com/
2009/12/20/email-address-validation/
1107                  * @copyright
2009-2010 Michael Rushton
1108                  * Feel free to use and redistribute
this code. But please keep this copyright notice.
1109                  */

1110                 
return (boolean)preg_match(
1111                     
'/^(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){255,})(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){65,}@)' .
1112                     
'((?>(?>(?>((?>(?>(?>\x0D\x0A)?[\t ])+|(?>[\t ]*\x0D\x0A)?[\t ]+)?)(\((?>(?2)' .
1113                     
'(?>[\x01-\x08\x0B\x0C\x0E-\'*-\[\]-\x7F]|\\\[\x00-\x7F]|(?3)))*(?2)\)))+(?2))|(?2))?)' .
1114                     
'([!#-\'*+\/-9=?^-~-]+|"(?>(?2)(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\x7F]))*' .
1115                     
'(?2)")(?>(?1)\.(?1)(?4))*(?1)@(?!(?1)[a-z0-9-]{64,})(?1)(?>([a-z0-9](?>[a-z0-9-]*[a-z0-9])?)' .
1116                     
'(?>(?1)\.(?!(?1)[a-z0-9-]{64,})(?1)(?5)){0,126}|\[(?:(?>IPv6:(?>([a-f0-9]{1,4})(?>:(?6)){7}' .
1117                     
'|(?!(?:.*[a-f0-9][:\]]){8,})((?6)(?>:(?6)){0,6})?::(?7)?))|(?>(?>IPv6:(?>(?6)(?>:(?6)){5}:' .
1118                     
'|(?!(?:.*[a-f0-9]:){6,})(?8)?::(?>((?6)(?>:(?6)){0,4}):)?))?(25[0-5]|2[0-4][0-9]|1[0-9]{2}' .
1119                     
'|[1-9]?[0-9])(?>\.(?9)){3}))\])(?1)$/isD',
1120                     $address
1121                 );
1122             
case 'pcre':
1123                 
//An older regex that doesn't need a recent PCRE
1124                 
return (boolean)preg_match(
1125                     
'/^(?!(?>"?(?>\\\[ -~]|[^"])"?){255,})(?!(?>"?(?>\\\[ -~]|[^"])"?){65,}@)(?>' .
1126                     
'[!#-\'*+\/-9=?^-~-]+|"(?>(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\xFF]))*")' .
1127                     
'(?>\.(?>[!#-\'*+\/-9=?^-~-]+|"(?>(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\xFF]))*"))*' .
1128                     
'@(?>(?![a-z0-9-]{64,})(?>[a-z0-9](?>[a-z0-9-]*[a-z0-9])?)(?>\.(?![a-z0-9-]{64,})' .
1129                     
'(?>[a-z0-9](?>[a-z0-9-]*[a-z0-9])?)){0,126}|\[(?:(?>IPv6:(?>(?>[a-f0-9]{1,4})(?>:' .
1130                     
'[a-f0-9]{1,4}){7}|(?!(?:.*[a-f0-9][:\]]){8,})(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,6})?' .
1131                     
'::(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,6})?))|(?>(?>IPv6:(?>[a-f0-9]{1,4}(?>:' .
1132                     
'[a-f0-9]{1,4}){5}:|(?!(?:.*[a-f0-9]:){6,})(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,4})?' .
1133                     
'::(?>(?:[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,4}):)?))?(?>25[0-5]|2[0-4][0-9]|1[0-9]{2}' .
1134                     
'|[1-9]?[0-9])(?>\.(?>25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}))\])$/isD',
1135                     $address
1136                 );
1137             
case 'html5':
1138                 
/**
1139                  * This
is the pattern used in the HTML5 spec for validation of 'email' type form input elements.
1140                  * @link http://www.whatwg.org/specs/web-apps/current-work/#e-mail-state-(type=email)
1141                  */

1142                 
return (boolean)preg_match(
1143                     
'/^[a-zA-Z0-9.!#$%&\'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}' .
1144                     
'[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/sD',
1145                     $address
1146                 );
1147             
case 'noregex':
1148                 
//No PCRE! Do something _very_ approximate!
1149                 
//Check the address is 3 chars or longer and contains an @ that's not the first or last char
1150                 
return (strlen($address) >= 3
1151                     and strpos($address,
'@') >= 1
1152                     and strpos($address,
'@') != strlen($address) - 1);
1153             
case 'php':
1154             
default:
1155                 
return (boolean)filter_var($address, FILTER_VALIDATE_EMAIL);
1156         }
1157     }
1158
1159     
/**
1160      * Tells whether IDNs (Internationalized Domain Names) are supported or not. This requires the
1161      *
"intl" and "mbstring" PHP extensions.
1162      * @
return bool "true" if required functions for IDN support are present
1163      */

1164     
public function idnSupported()
1165     {
1166         
// @TODO: Write our own "idn_to_ascii" function for PHP <= 5.2.
1167         
return function_exists('idn_to_ascii') and function_exists('mb_convert_encoding');
1168     }
1169
1170     
/**
1171      * Converts IDN
in given email address to its ASCII form, also known as punycode, if possible.
1172      * Important: Address must be passed
in same encoding as currently set in PHPMailer::$CharSet.
1173      * This function silently returns unmodified address
if:
1174      * - No conversion
is necessary (i.e. domain name is not an IDN, or is already in ASCII form)
1175      * - Conversion to punycode
is impossible (e.g. required PHP functions are not available)
1176      * or fails
for any reason (e.g. domain has characters not allowed in an IDN)
1177      * @see PHPMailer::$CharSet
1178      * @param
string $address The email address to convert
1179      * @
return string The encoded address in ASCII form
1180      */

1181     
public function punyencodeAddress($address)
1182     {
1183         
// Verify we have required functions, CharSet, and at-sign.
1184         
if ($this->idnSupported() and
1185             !empty($
this->CharSet) and
1186             ($pos = strrpos($address,
'@')) !== false) {
1187             $domain = substr($address, ++$pos);
1188             
// Verify CharSet string is a valid one, and domain properly encoded in this CharSet.
1189             
if ($this->has8bitChars($domain) and @mb_check_encoding($domain, $this->CharSet)) {
1190                 $domain = mb_convert_encoding($domain,
'UTF-8', $this->CharSet);
1191                 
if (($punycode = defined('INTL_IDNA_VARIANT_UTS46') ?
1192                     idn_to_ascii($domain,
0, INTL_IDNA_VARIANT_UTS46) :
1193                     idn_to_ascii($domain)) !==
false) {
1194                     
return substr($address, 0, $pos) . $punycode;
1195                 }
1196             }
1197         }
1198         
return $address;
1199     }
1200
1201     
/**
1202      * Create a message and send it.
1203      * Uses the sending method specified
by $Mailer.
1204      * @throws phpmailerException
1205      * @
return boolean false on error - See the ErrorInfo property for details of the error.
1206      */

1207     
public function send()
1208     {
1209         
try {
1210             
if (!$this->preSend()) {
1211                 
return false;
1212             }
1213             
return $this->postSend();
1214         }
catch (phpmailerException $exc) {
1215             $
this->mailHeader = '';
1216             $
this->setError($exc->getMessage());
1217             
if ($this->exceptions) {
1218                 
throw $exc;
1219             }
1220             
return false;
1221         }
1222     }
1223
1224     
/**
1225      * Prepare a message
for sending.
1226      * @throws phpmailerException
1227      * @
return boolean
1228      */

1229     
public function preSend()
1230     {
1231         
try {
1232             $
this->error_count = 0; // Reset errors
1233             $
this->mailHeader = '';
1234
1235             
// Dequeue recipient and Reply-To addresses with IDN
1236             
foreach (array_merge($this->RecipientsQueue, $this->ReplyToQueue) as $params) {
1237                 $
params[1] = $this->punyencodeAddress($params[1]);
1238                 call_user_func_array(array($
this, 'addAnAddress'), $params);
1239             }
1240             
if ((count($this->to) + count($this->cc) + count($this->bcc)) < 1) {
1241                 
throw new phpmailerException($this->lang('provide_address'), self::STOP_CRITICAL);
1242             }
1243
1244             
// Validate From, Sender, and ConfirmReadingTo addresses
1245             
foreach (array('From', 'Sender', 'ConfirmReadingTo') as $address_kind) {
1246                 $
this->$address_kind = trim($this->$address_kind);
1247                 
if (empty($this->$address_kind)) {
1248                     
continue;
1249                 }
1250                 $
this->$address_kind = $this->punyencodeAddress($this->$address_kind);
1251                 
if (!$this->validateAddress($this->$address_kind)) {
1252                     $error_message = $
this->lang('invalid_address') . ' (punyEncode) ' . $this->$address_kind;
1253                     $
this->setError($error_message);
1254                     $
this->edebug($error_message);
1255                     
if ($this->exceptions) {
1256                         
throw new phpmailerException($error_message);
1257                     }
1258                     
return false;
1259                 }
1260             }
1261
1262             
// Set whether the message is multipart/alternative
1263             
if ($this->alternativeExists()) {
1264                 $
this->ContentType = 'multipart/alternative';
1265             }
1266
1267             $
this->setMessageType();
1268             
// Refuse to send an empty message unless we are specifically allowing it
1269             
if (!$this->AllowEmpty and empty($this->Body)) {
1270                 
throw new phpmailerException($this->lang('empty_message'), self::STOP_CRITICAL);
1271             }
1272
1273             
// Create body before headers in case body makes changes to headers (e.g. altering transfer encoding)
1274             $
this->MIMEHeader = '';
1275             $
this->MIMEBody = $this->createBody();
1276             
// createBody may have added some headers, so retain them
1277             $tempheaders = $
this->MIMEHeader;
1278             $
this->MIMEHeader = $this->createHeader();
1279             $
this->MIMEHeader .= $tempheaders;
1280
1281             
// To capture the complete message when using mail(), create
1282             
// an extra header list which createHeader() doesn't fold in
1283             
if ($this->Mailer == 'mail') {
1284                 
if (count($this->to) > 0) {
1285                     $
this->mailHeader .= $this->addrAppend('To', $this->to);
1286                 }
else {
1287                     $
this->mailHeader .= $this->headerLine('To', 'undisclosed-recipients:;');
1288                 }
1289                 $
this->mailHeader .= $this->headerLine(
1290                     
'Subject',
1291                     $
this->encodeHeader($this->secureHeader(trim($this->Subject)))
1292                 );
1293             }
1294
1295             
// Sign with DKIM if enabled
1296             
if (!empty($this->DKIM_domain)
1297                 && !empty($
this->DKIM_selector)
1298                 && (!empty($
this->DKIM_private_string)
1299                    || (!empty($
this->DKIM_private) && file_exists($this->DKIM_private))
1300                 )
1301             ) {
1302                 $header_dkim = $
this->DKIM_Add(
1303                     $
this->MIMEHeader . $this->mailHeader,
1304                     $
this->encodeHeader($this->secureHeader($this->Subject)),
1305                     $
this->MIMEBody
1306                 );
1307                 $
this->MIMEHeader = rtrim($this->MIMEHeader, "\r\n ") . self::CRLF .
1308                     str_replace(
"\r\n", "\n", $header_dkim) . self::CRLF;
1309             }
1310             
return true;
1311         }
catch (phpmailerException $exc) {
1312             $
this->setError($exc->getMessage());
1313             
if ($this->exceptions) {
1314                 
throw $exc;
1315             }
1316             
return false;
1317         }
1318     }
1319
1320     
/**
1321      * Actually send a message.
1322      * Send the email via the selected mechanism
1323      * @throws phpmailerException
1324      * @
return boolean
1325      */

1326     
public function postSend()
1327     {
1328         
try {
1329             
// Choose the mailer and send through it
1330             
switch ($this->Mailer) {
1331                 
case 'sendmail':
1332                 
case 'qmail':
1333                     
return $this->sendmailSend($this->MIMEHeader, $this->MIMEBody);
1334                 
case 'smtp':
1335                     
return $this->smtpSend($this->MIMEHeader, $this->MIMEBody);
1336                 
case 'mail':
1337                     
return $this->mailSend($this->MIMEHeader, $this->MIMEBody);
1338                 
default:
1339                     $sendMethod = $
this->Mailer.'Send';
1340                     
if (method_exists($this, $sendMethod)) {
1341                         
return $this->$sendMethod($this->MIMEHeader, $this->MIMEBody);
1342                     }
1343
1344                     
return $this->mailSend($this->MIMEHeader, $this->MIMEBody);
1345             }
1346         }
catch (phpmailerException $exc) {
1347             $
this->setError($exc->getMessage());
1348             $
this->edebug($exc->getMessage());
1349             
if ($this->exceptions) {
1350                 
throw $exc;
1351             }
1352         }
1353         
return false;
1354     }
1355
1356     
/**
1357      * Send mail
using the $Sendmail program.
1358      * @param
string $header The message headers
1359      * @param
string $body The message body
1360      * @see PHPMailer::$Sendmail
1361      * @throws phpmailerException
1362      * @access
protected
1363      * @
return boolean
1364      */

1365     
protected function sendmailSend($header, $body)
1366     {
1367         
// CVE-2016-10033, CVE-2016-10045: Don't pass -f if characters will be escaped.
1368         
if (!empty($this->Sender) and self::isShellSafe($this->Sender)) {
1369             
if ($this->Mailer == 'qmail') {
1370                 $sendmailFmt =
'%s -f%s';
1371             }
else {
1372                 $sendmailFmt =
'%s -oi -f%s -t';
1373             }
1374         }
else {
1375             
if ($this->Mailer == 'qmail') {
1376                 $sendmailFmt =
'%s';
1377             }
else {
1378                 $sendmailFmt =
'%s -oi -t';
1379             }
1380         }
1381
1382         
// TODO: If possible, this should be changed to escapeshellarg. Needs thorough testing.
1383         $sendmail = sprintf($sendmailFmt, escapeshellcmd($
this->Sendmail), $this->Sender);
1384
1385         
if ($this->SingleTo) {
1386             
foreach ($this->SingleToArray as $toAddr) {
1387                 
if (!@$mail = popen($sendmail, 'w')) {
1388                     
throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
1389                 }
1390                 fputs($mail,
'To: ' . $toAddr . "\n");
1391                 fputs($mail, $header);
1392                 fputs($mail, $body);
1393                 $result = pclose($mail);
1394                 $
this->doCallback(
1395                     ($result ==
0),
1396                     array($toAddr),
1397                     $
this->cc,
1398                     $
this->bcc,
1399                     $
this->Subject,
1400                     $body,
1401                     $
this->From
1402                 );
1403                 
if ($result != 0) {
1404                     
throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
1405                 }
1406             }
1407         }
else {
1408             
if (!@$mail = popen($sendmail, 'w')) {
1409                 
throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
1410             }
1411             fputs($mail, $header);
1412             fputs($mail, $body);
1413             $result = pclose($mail);
1414             $
this->doCallback(
1415                 ($result ==
0),
1416                 $
this->to,
1417                 $
this->cc,
1418                 $
this->bcc,
1419                 $
this->Subject,
1420                 $body,
1421                 $
this->From
1422             );
1423             
if ($result != 0) {
1424                 
throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
1425             }
1426         }
1427         
return true;
1428     }
1429
1430     
/**
1431      * Fix CVE-
2016-10033 and CVE-2016-10045 by disallowing potentially unsafe shell characters.
1432      *
1433      * Note that escapeshellarg and escapeshellcmd are inadequate
for our purposes, especially on Windows.
1434      * @param
string $string The string to be validated
1435      * @see https://github.com/PHPMailer/PHPMailer/issues/
924 CVE-2016-10045 bug report
1436      * @access
protected
1437      * @
return boolean
1438      */

1439     
protected static function isShellSafe($string)
1440     {
1441         
// Future-proof
1442         
if (escapeshellcmd($string) !== $string
1443             or !in_array(escapeshellarg($
string), array("'$string'", "\"$string\""))
1444         ) {
1445             
return false;
1446         }
1447
1448         $length = strlen($
string);
1449
1450         
for ($i = 0; $i < $length; $i++) {
1451             $c = $
string[$i];
1452
1453             
// All other characters have a special meaning in at least one common shell, including = and +.
1454             
// Full stop (.) has a special meaning in cmd.exe, but its impact should be negligible here.
1455             
// Note that this does permit non-Latin alphanumeric characters based on the current locale.
1456             
if (!ctype_alnum($c) && strpos('@_-.', $c) === false) {
1457                 
return false;
1458             }
1459         }
1460
1461         
return true;
1462     }
1463
1464     
/**
1465      * Send mail
using the PHP mail() function.
1466      * @param
string $header The message headers
1467      * @param
string $body The message body
1468      * @link http://www.php.net/manual/en/book.mail.php
1469      * @throws phpmailerException
1470      * @access
protected
1471      * @
return boolean
1472      */

1473     
protected function mailSend($header, $body)
1474     {
1475         $toArr = array();
1476         
foreach ($this->to as $toaddr) {
1477             $toArr[] = $
this->addrFormat($toaddr);
1478         }
1479         $to = implode(
', ', $toArr);
1480
1481         $
params = null;
1482         
//This sets the SMTP envelope sender which gets turned into a return-path header by the receiver
1483         
if (!empty($this->Sender) and $this->validateAddress($this->Sender)) {
1484             
// CVE-2016-10033, CVE-2016-10045: Don't pass -f if characters will be escaped.
1485             
if (self::isShellSafe($this->Sender)) {
1486                 $
params = sprintf('-f%s', $this->Sender);
1487             }
1488         }
1489         
if (!empty($this->Sender) and !ini_get('safe_mode') and $this->validateAddress($this->Sender)) {
1490             $old_from = ini_get(
'sendmail_from');
1491             ini_set(
'sendmail_from', $this->Sender);
1492         }
1493         $result =
false;
1494         
if ($this->SingleTo and count($toArr) > 1) {
1495             
foreach ($toArr as $toAddr) {
1496                 $result = $
this->mailPassthru($toAddr, $this->Subject, $body, $header, $params);
1497                 $
this->doCallback($result, array($toAddr), $this->cc, $this->bcc, $this->Subject, $body, $this->From);
1498             }
1499         }
else {
1500             $result = $
this->mailPassthru($to, $this->Subject, $body, $header, $params);
1501             $
this->doCallback($result, $this->to, $this->cc, $this->bcc, $this->Subject, $body, $this->From);
1502         }
1503         
if (isset($old_from)) {
1504             ini_set(
'sendmail_from', $old_from);
1505         }
1506         
if (!$result) {
1507             
throw new phpmailerException($this->lang('instantiate'), self::STOP_CRITICAL);
1508         }
1509         
return true;
1510     }
1511
1512     
/**
1513      * Get an instance to use
for SMTP operations.
1514      * Override
this function to load your own SMTP implementation
1515      * @
return SMTP
1516      */

1517     
public function getSMTPInstance()
1518     {
1519         
if (!is_object($this->smtp)) {
1520             $
this->smtp = new SMTP;
1521         }
1522         
return $this->smtp;
1523     }
1524
1525     
/**
1526      * Send mail via SMTP.
1527      * Returns
false if there is a bad MAIL FROM, RCPT, or DATA input.
1528      * Uses the PHPMailerSMTP
class by default.
1529      * @see PHPMailer::getSMTPInstance() to use a different
class.
1530      * @param
string $header The message headers
1531      * @param
string $body The message body
1532      * @throws phpmailerException
1533      * @uses SMTP
1534      * @access
protected
1535      * @
return boolean
1536      */

1537     
protected function smtpSend($header, $body)
1538     {
1539         $bad_rcpt = array();
1540         
if (!$this->smtpConnect($this->SMTPOptions)) {
1541             
throw new phpmailerException($this->lang('smtp_connect_failed'), self::STOP_CRITICAL);
1542         }
1543         
if (!empty($this->Sender) and $this->validateAddress($this->Sender)) {
1544             $smtp_from = $
this->Sender;
1545         }
else {
1546             $smtp_from = $
this->From;
1547         }
1548         
if (!$this->smtp->mail($smtp_from)) {
1549             $
this->setError($this->lang('from_failed') . $smtp_from . ' : ' . implode(',', $this->smtp->getError()));
1550             
throw new phpmailerException($this->ErrorInfo, self::STOP_CRITICAL);
1551         }
1552
1553         
// Attempt to send to all recipients
1554         
foreach (array($this->to, $this->cc, $this->bcc) as $togroup) {
1555             
foreach ($togroup as $to) {
1556                 
if (!$this->smtp->recipient($to[0])) {
1557                     $error = $
this->smtp->getError();
1558                     $bad_rcpt[] = array(
'to' => $to[0], 'error' => $error['detail']);
1559                     $isSent =
false;
1560                 }
else {
1561                     $isSent =
true;
1562                 }
1563                 $
this->doCallback($isSent, array($to[0]), array(), array(), $this->Subject, $body, $this->From);
1564             }
1565         }
1566
1567         
// Only send the DATA command if we have viable recipients
1568         
if ((count($this->all_recipients) > count($bad_rcpt)) and !$this->smtp->data($header . $body)) {
1569             
throw new phpmailerException($this->lang('data_not_accepted'), self::STOP_CRITICAL);
1570         }
1571         
if ($this->SMTPKeepAlive) {
1572             $
this->smtp->reset();
1573         }
else {
1574             $
this->smtp->quit();
1575             $
this->smtp->close();
1576         }
1577         
//Create error message for any bad addresses
1578         
if (count($bad_rcpt) > 0) {
1579             $errstr =
'';
1580             
foreach ($bad_rcpt as $bad) {
1581                 $errstr .= $bad[
'to'] . ': ' . $bad['error'];
1582             }
1583             
throw new phpmailerException(
1584                 $
this->lang('recipients_failed') . $errstr,
1585                 self::STOP_CONTINUE
1586             );
1587         }
1588         
return true;
1589     }
1590
1591     
/**
1592      * Initiate a connection to an SMTP server.
1593      * Returns
false if the operation failed.
1594      * @param array $options An array of options compatible with stream_context_create()
1595      * @uses SMTP
1596      * @access
public
1597      * @throws phpmailerException
1598      * @
return boolean
1599      */

1600     
public function smtpConnect($options = null)
1601     {
1602         
if (is_null($this->smtp)) {
1603             $
this->smtp = $this->getSMTPInstance();
1604         }
1605
1606         
//If no options are provided, use whatever is set in the instance
1607         
if (is_null($options)) {
1608             $options = $
this->SMTPOptions;
1609         }
1610
1611         
// Already connected?
1612         
if ($this->smtp->connected()) {
1613             
return true;
1614         }
1615
1616         $
this->smtp->setTimeout($this->Timeout);
1617         $
this->smtp->setDebugLevel($this->SMTPDebug);
1618         $
this->smtp->setDebugOutput($this->Debugoutput);
1619         $
this->smtp->setVerp($this->do_verp);
1620         $hosts = explode(
';', $this->Host);
1621         $lastexception =
null;
1622
1623         
foreach ($hosts as $hostentry) {
1624             $hostinfo = array();
1625             
if (!preg_match('/^((ssl|tls):\/\/)*([a-zA-Z0-9\.-]*):?([0-9]*)$/', trim($hostentry), $hostinfo)) {
1626                 
// Not a valid host entry
1627                 
continue;
1628             }
1629             
// $hostinfo[2]: optional ssl or tls prefix
1630             
// $hostinfo[3]: the hostname
1631             
// $hostinfo[4]: optional port number
1632             
// The host string prefix can temporarily override the current setting for SMTPSecure
1633             
// If it's not specified, the default value is used
1634             $prefix =
'';
1635             $secure = $
this->SMTPSecure;
1636             $tls = ($
this->SMTPSecure == 'tls');
1637             
if ('ssl' == $hostinfo[2] or ('' == $hostinfo[2] and 'ssl' == $this->SMTPSecure)) {
1638                 $prefix =
'ssl://';
1639                 $tls =
false; // Can't have SSL and TLS at the same time
1640                 $secure =
'ssl';
1641             } elseif ($hostinfo[
2] == 'tls') {
1642                 $tls =
true;
1643                 
// tls doesn't use a prefix
1644                 $secure =
'tls';
1645             }
1646             
//Do we need the OpenSSL extension?
1647             $sslext = defined(
'OPENSSL_ALGO_SHA1');
1648             
if ('tls' === $secure or 'ssl' === $secure) {
1649                 
//Check for an OpenSSL constant rather than using extension_loaded, which is sometimes disabled
1650                 
if (!$sslext) {
1651                     
throw new phpmailerException($this->lang('extension_missing').'openssl', self::STOP_CRITICAL);
1652                 }
1653             }
1654             $host = $hostinfo[
3];
1655             $port = $
this->Port;
1656             $tport = (integer)$hostinfo[
4];
1657             
if ($tport > 0 and $tport < 65536) {
1658                 $port = $tport;
1659             }
1660             
if ($this->smtp->connect($prefix . $host, $port, $this->Timeout, $options)) {
1661                 
try {
1662                     
if ($this->Helo) {
1663                         $hello = $
this->Helo;
1664                     }
else {
1665                         $hello = $
this->serverHostname();
1666                     }
1667                     $
this->smtp->hello($hello);
1668                     
//Automatically enable TLS encryption if:
1669                     
// * it's not disabled
1670                     
// * we have openssl extension
1671                     
// * we are not already using SSL
1672                     
// * the server offers STARTTLS
1673                     
if ($this->SMTPAutoTLS and $sslext and $secure != 'ssl' and $this->smtp->getServerExt('STARTTLS')) {
1674                         $tls =
true;
1675                     }
1676                     
if ($tls) {
1677                         
if (!$this->smtp->startTLS()) {
1678                             
throw new phpmailerException($this->lang('connect_host'));
1679                         }
1680                         
// We must resend EHLO after TLS negotiation
1681                         $
this->smtp->hello($hello);
1682                     }
1683                     
if ($this->SMTPAuth) {
1684                         
if (!$this->smtp->authenticate(
1685                             $
this->Username,
1686                             $
this->Password,
1687                             $
this->AuthType,
1688                             $
this->Realm,
1689                             $
this->Workstation
1690                         )
1691                         ) {
1692                             
throw new phpmailerException($this->lang('authenticate'));
1693                         }
1694                     }
1695                     
return true;
1696                 }
catch (phpmailerException $exc) {
1697                     $lastexception = $exc;
1698                     $
this->edebug($exc->getMessage());
1699                     
// We must have connected, but then failed TLS or Auth, so close connection nicely
1700                     $
this->smtp->quit();
1701                 }
1702             }
1703         }
1704         
// If we get here, all connection attempts have failed, so close connection hard
1705         $
this->smtp->close();
1706         
// As we've caught all exceptions, just report whatever the last one was
1707         
if ($this->exceptions and !is_null($lastexception)) {
1708             
throw $lastexception;
1709         }
1710         
return false;
1711     }
1712
1713     
/**
1714      * Close the active SMTP session
if one exists.
1715      * @
return void
1716      */

1717     
public function smtpClose()
1718     {
1719         
if (is_a($this->smtp, 'SMTP')) {
1720             
if ($this->smtp->connected()) {
1721                 $
this->smtp->quit();
1722                 $
this->smtp->close();
1723             }
1724         }
1725     }
1726
1727     
/**
1728      * Set the language
for error messages.
1729      * Returns
false if it cannot load the language file.
1730      * The
default language is English.
1731      * @param
string $langcode ISO 639-1 2-character language code (e.g. French is "fr")
1732      * @param
string $lang_path Path to the language file directory, with trailing separator (slash)
1733      * @
return boolean
1734      * @access
public
1735      */

1736     
public function setLanguage($langcode = 'en', $lang_path = '')
1737     {
1738         
// Backwards compatibility for renamed language codes
1739         $renamed_langcodes = array(
1740             
'br' => 'pt_br',
1741             
'cz' => 'cs',
1742             
'dk' => 'da',
1743             
'no' => 'nb',
1744             
'se' => 'sv',
1745         );
1746
1747         
if (isset($renamed_langcodes[$langcode])) {
1748             $langcode = $renamed_langcodes[$langcode];
1749         }
1750
1751         
// Define full set of translatable strings in English
1752         $PHPMAILER_LANG = array(
1753             
'authenticate' => 'SMTP Error: Could not authenticate.',
1754             
'connect_host' => 'SMTP Error: Could not connect to SMTP host.',
1755             
'data_not_accepted' => 'SMTP Error: data not accepted.',
1756             
'empty_message' => 'Message body empty',
1757             
'encoding' => 'Unknown encoding: ',
1758             
'execute' => 'Could not execute: ',
1759             
'file_access' => 'Could not access file: ',
1760             
'file_open' => 'File Error: Could not open file: ',
1761             
'from_failed' => 'The following From address failed: ',
1762             
'instantiate' => 'Could not instantiate mail function.',
1763             
'invalid_address' => 'Invalid address: ',
1764             
'mailer_not_supported' => ' mailer is not supported.',
1765             
'provide_address' => 'You must provide at least one recipient email address.',
1766             
'recipients_failed' => 'SMTP Error: The following recipients failed: ',
1767             
'signing' => 'Signing Error: ',
1768             
'smtp_connect_failed' => 'SMTP connect() failed.',
1769             
'smtp_error' => 'SMTP server error: ',
1770             
'variable_set' => 'Cannot set or reset variable: ',
1771             
'extension_missing' => 'Extension missing: '
1772         );
1773         
if (empty($lang_path)) {
1774             
// Calculate an absolute path so it can work if CWD is not here
1775             $lang_path = dirname(__FILE__). DIRECTORY_SEPARATOR .
'language'. DIRECTORY_SEPARATOR;
1776         }
1777         
//Validate $langcode
1778         
if (!preg_match('/^[a-z]{2}(?:_[a-zA-Z]{2})?$/', $langcode)) {
1779             $langcode =
'en';
1780         }
1781         $foundlang =
true;
1782         $lang_file = $lang_path .
'phpmailer.lang-' . $langcode . '.php';
1783         
// There is no English translation file
1784         
if ($langcode != 'en') {
1785             
// Make sure language file path is readable
1786             
if (!is_readable($lang_file)) {
1787                 $foundlang =
false;
1788             }
else {
1789                 
// Overwrite language-specific strings.
1790                 
// This way we'll never have missing translation keys.
1791                 $foundlang = include $lang_file;
1792             }
1793         }
1794         $
this->language = $PHPMAILER_LANG;
1795         
return (boolean)$foundlang; // Returns false if language not found
1796     }
1797
1798     
/**
1799      * Get the array of strings
for the current language.
1800      * @
return array
1801      */

1802     
public function getTranslations()
1803     {
1804         
return $this->language;
1805     }
1806
1807     
/**
1808      * Create recipient headers.
1809      * @access
public
1810      * @param
string $type
1811      * @param array $addr An array of recipient,
1812      *
where each recipient is a 2-element indexed array with element 0 containing an address
1813      * and element
1 containing a name, like:
1814      * array(array(
'joe@example.com', 'Joe User'), array('zoe@example.com', 'Zoe User'))
1815      * @
return string
1816      */

1817     
public function addrAppend($type, $addr)
1818     {
1819         $addresses = array();
1820         
foreach ($addr as $address) {
1821             $addresses[] = $
this->addrFormat($address);
1822         }
1823         
return $type . ': ' . implode(', ', $addresses) . $this->LE;
1824     }
1825
1826     
/**
1827      * Format an address
for use in a message header.
1828      * @access
public
1829      * @param array $addr A
2-element indexed array, element 0 containing an address, element 1 containing a name
1830      * like array(
'joe@example.com', 'Joe User')
1831      * @
return string
1832      */

1833     
public function addrFormat($addr)
1834     {
1835         
if (empty($addr[1])) { // No name provided
1836             
return $this->secureHeader($addr[0]);
1837         }
else {
1838             
return $this->encodeHeader($this->secureHeader($addr[1]), 'phrase') . ' <' . $this->secureHeader(
1839                 $addr[
0]
1840             ) .
'>';
1841         }
1842     }
1843
1844     
/**
1845      * Word-wrap message.
1846      * For use with mailers that
do not automatically perform wrapping
1847      * and
for quoted-printable encoded messages.
1848      * Original written
by philippe.
1849      * @param
string $message The message to wrap
1850      * @param integer $length The line length to wrap to
1851      * @param boolean $qp_mode Whether to run
in Quoted-Printable mode
1852      * @access
public
1853      * @
return string
1854      */

1855     
public function wrapText($message, $length, $qp_mode = false)
1856     {
1857         
if ($qp_mode) {
1858             $soft_break = sprintf(
' =%s', $this->LE);
1859         }
else {
1860             $soft_break = $
this->LE;
1861         }
1862         
// If utf-8 encoding is used, we will need to make sure we don't
1863         
// split multibyte characters when we wrap
1864         $is_utf8 = (strtolower($
this->CharSet) == 'utf-8');
1865         $lelen = strlen($
this->LE);
1866         $crlflen = strlen(self::CRLF);
1867
1868         $message = $
this->fixEOL($message);
1869         
//Remove a trailing line break
1870         
if (substr($message, -$lelen) == $this->LE) {
1871             $message = substr($message,
0, -$lelen);
1872         }
1873
1874         
//Split message into lines
1875         $lines = explode($
this->LE, $message);
1876         
//Message will be rebuilt in here
1877         $message =
'';
1878         
foreach ($lines as $line) {
1879             $words = explode(
' ', $line);
1880             $buf =
'';
1881             $firstword =
true;
1882             
foreach ($words as $word) {
1883                 
if ($qp_mode and (strlen($word) > $length)) {
1884                     $space_left = $length - strlen($buf) - $crlflen;
1885                     
if (!$firstword) {
1886                         
if ($space_left > 20) {
1887                             $len = $space_left;
1888                             
if ($is_utf8) {
1889                                 $len = $
this->utf8CharBoundary($word, $len);
1890                             } elseif (substr($word, $len -
1, 1) == '=') {
1891                                 $len--;
1892                             } elseif (substr($word, $len -
2, 1) == '=') {
1893                                 $len -=
2;
1894                             }
1895                             $part = substr($word,
0, $len);
1896                             $word = substr($word, $len);
1897                             $buf .=
' ' . $part;
1898                             $message .= $buf . sprintf(
'=%s', self::CRLF);
1899                         }
else {
1900                             $message .= $buf . $soft_break;
1901                         }
1902                         $buf =
'';
1903                     }
1904                     
while (strlen($word) > 0) {
1905                         
if ($length <= 0) {
1906                             
break;
1907                         }
1908                         $len = $length;
1909                         
if ($is_utf8) {
1910                             $len = $
this->utf8CharBoundary($word, $len);
1911                         } elseif (substr($word, $len -
1, 1) == '=') {
1912                             $len--;
1913                         } elseif (substr($word, $len -
2, 1) == '=') {
1914                             $len -=
2;
1915                         }
1916                         $part = substr($word,
0, $len);
1917                         $word = substr($word, $len);
1918
1919                         
if (strlen($word) > 0) {
1920                             $message .= $part . sprintf(
'=%s', self::CRLF);
1921                         }
else {
1922                             $buf = $part;
1923                         }
1924                     }
1925                 }
else {
1926                     $buf_o = $buf;
1927                     
if (!$firstword) {
1928                         $buf .=
' ';
1929                     }
1930                     $buf .= $word;
1931
1932                     
if (strlen($buf) > $length and $buf_o != '') {
1933                         $message .= $buf_o . $soft_break;
1934                         $buf = $word;
1935                     }
1936                 }
1937                 $firstword =
false;
1938             }
1939             $message .= $buf . self::CRLF;
1940         }
1941
1942         
return $message;
1943     }
1944
1945     
/**
1946      * Find the last character boundary prior to $maxLength
in a utf-8
1947      * quoted-printable encoded
string.
1948      * Original written
by Colin Brown.
1949      * @access
public
1950      * @param
string $encodedText utf-8 QP text
1951      * @param integer $maxLength Find the last character boundary prior to
this length
1952      * @
return integer
1953      */

1954     
public function utf8CharBoundary($encodedText, $maxLength)
1955     {
1956         $foundSplitPos =
false;
1957         $lookBack =
3;
1958         
while (!$foundSplitPos) {
1959             $lastChunk = substr($encodedText, $maxLength - $lookBack, $lookBack);
1960             $encodedCharPos = strpos($lastChunk,
'=');
1961             
if (false !== $encodedCharPos) {
1962                 
// Found start of encoded character byte within $lookBack block.
1963                 
// Check the encoded byte value (the 2 chars after the '=')
1964                 $hex = substr($encodedText, $maxLength - $lookBack + $encodedCharPos +
1, 2);
1965                 $dec = hexdec($hex);
1966                 
if ($dec < 128) {
1967                     
// Single byte character.
1968                     
// If the encoded char was found at pos 0, it will fit
1969                     
// otherwise reduce maxLength to start of the encoded char
1970                     
if ($encodedCharPos > 0) {
1971                         $maxLength = $maxLength - ($lookBack - $encodedCharPos);
1972                     }
1973                     $foundSplitPos =
true;
1974                 } elseif ($dec >=
192) {
1975                     
// First byte of a multi byte character
1976                     
// Reduce maxLength to split at start of character
1977                     $maxLength = $maxLength - ($lookBack - $encodedCharPos);
1978                     $foundSplitPos =
true;
1979                 } elseif ($dec <
192) {
1980                     
// Middle byte of a multi byte character, look further back
1981                     $lookBack +=
3;
1982                 }
1983             }
else {
1984                 
// No encoded character found
1985                 $foundSplitPos =
true;
1986             }
1987         }
1988         
return $maxLength;
1989     }
1990
1991     
/**
1992      * Apply word wrapping to the message body.
1993      * Wraps the message body to the number of chars
set in the WordWrap property.
1994      * You should only
do this to plain-text bodies as wrapping HTML tags may break them.
1995      * This
is called automatically by createBody(), so you don't need to call it yourself.
1996      * @access
public
1997      * @
return void
1998      */

1999     
public function setWordWrap()
2000     {
2001         
if ($this->WordWrap < 1) {
2002             
return;
2003         }
2004
2005         
switch ($this->message_type) {
2006             
case 'alt':
2007             
case 'alt_inline':
2008             
case 'alt_attach':
2009             
case 'alt_inline_attach':
2010                 $
this->AltBody = $this->wrapText($this->AltBody, $this->WordWrap);
2011                 
break;
2012             
default:
2013                 $
this->Body = $this->wrapText($this->Body, $this->WordWrap);
2014                 
break;
2015         }
2016     }
2017
2018     
/**
2019      * Assemble message headers.
2020      * @access
public
2021      * @
return string The assembled headers
2022      */

2023     
public function createHeader()
2024     {
2025         $result =
'';
2026
2027         $result .= $
this->headerLine('Date', $this->MessageDate == '' ? self::rfcDate() : $this->MessageDate);
2028
2029         
// To be created automatically by mail()
2030         
if ($this->SingleTo) {
2031             
if ($this->Mailer != 'mail') {
2032                 
foreach ($this->to as $toaddr) {
2033                     $
this->SingleToArray[] = $this->addrFormat($toaddr);
2034                 }
2035             }
2036         }
else {
2037             
if (count($this->to) > 0) {
2038                 
if ($this->Mailer != 'mail') {
2039                     $result .= $
this->addrAppend('To', $this->to);
2040                 }
2041             } elseif (count($
this->cc) == 0) {
2042                 $result .= $
this->headerLine('To', 'undisclosed-recipients:;');
2043             }
2044         }
2045
2046         $result .= $
this->addrAppend('From', array(array(trim($this->From), $this->FromName)));
2047
2048         
// sendmail and mail() extract Cc from the header before sending
2049         
if (count($this->cc) > 0) {
2050             $result .= $
this->addrAppend('Cc', $this->cc);
2051         }
2052
2053         
// sendmail and mail() extract Bcc from the header before sending
2054         
if ((
2055                 $
this->Mailer == 'sendmail' or $this->Mailer == 'qmail' or $this->Mailer == 'mail'
2056             )
2057             and count($
this->bcc) > 0
2058         ) {
2059             $result .= $
this->addrAppend('Bcc', $this->bcc);
2060         }
2061
2062         
if (count($this->ReplyTo) > 0) {
2063             $result .= $
this->addrAppend('Reply-To', $this->ReplyTo);
2064         }
2065
2066         
// mail() sets the subject itself
2067         
if ($this->Mailer != 'mail') {
2068             $result .= $
this->headerLine('Subject', $this->encodeHeader($this->secureHeader($this->Subject)));
2069         }
2070
2071         
// Only allow a custom message ID if it conforms to RFC 5322 section 3.6.4
2072         
// https://tools.ietf.org/html/rfc5322#section-3.6.4
2073         
if ('' != $this->MessageID and preg_match('/^<.*@.*>$/', $this->MessageID)) {
2074             $
this->lastMessageID = $this->MessageID;
2075         }
else {
2076             $
this->lastMessageID = sprintf('<%s@%s>', $this->uniqueid, $this->serverHostname());
2077         }
2078         $result .= $
this->headerLine('Message-ID', $this->lastMessageID);
2079         
if (!is_null($this->Priority)) {
2080             $result .= $
this->headerLine('X-Priority', $this->Priority);
2081         }
2082         
if ($this->XMailer == '') {
2083             $result .= $
this->headerLine(
2084                 
'X-Mailer',
2085                 
'PHPMailer ' . $this->Version . ' (https://github.com/PHPMailer/PHPMailer)'
2086             );
2087         }
else {
2088             $myXmailer = trim($
this->XMailer);
2089             
if ($myXmailer) {
2090                 $result .= $
this->headerLine('X-Mailer', $myXmailer);
2091             }
2092         }
2093
2094         
if ($this->ConfirmReadingTo != '') {
2095             $result .= $
this->headerLine('Disposition-Notification-To', '<' . $this->ConfirmReadingTo . '>');
2096         }
2097
2098         
// Add custom headers
2099         
foreach ($this->CustomHeader as $header) {
2100             $result .= $
this->headerLine(
2101                 trim($header[
0]),
2102                 $
this->encodeHeader(trim($header[1]))
2103             );
2104         }
2105         
if (!$this->sign_key_file) {
2106             $result .= $
this->headerLine('MIME-Version', '1.0');
2107             $result .= $
this->getMailMIME();
2108         }
2109
2110         
return $result;
2111     }
2112
2113     
/**
2114      * Get the message MIME type headers.
2115      * @access
public
2116      * @
return string
2117      */

2118     
public function getMailMIME()
2119     {
2120         $result =
'';
2121         $ismultipart =
true;
2122         
switch ($this->message_type) {
2123             
case 'inline':
2124                 $result .= $
this->headerLine('Content-Type', 'multipart/related;');
2125                 $result .= $
this->textLine("\tboundary=\"" . $this->boundary[1] . '"');
2126                 
break;
2127             
case 'attach':
2128             
case 'inline_attach':
2129             
case 'alt_attach':
2130             
case 'alt_inline_attach':
2131                 $result .= $
this->headerLine('Content-Type', 'multipart/mixed;');
2132                 $result .= $
this->textLine("\tboundary=\"" . $this->boundary[1] . '"');
2133                 
break;
2134             
case 'alt':
2135             
case 'alt_inline':
2136                 $result .= $
this->headerLine('Content-Type', 'multipart/alternative;');
2137                 $result .= $
this->textLine("\tboundary=\"" . $this->boundary[1] . '"');
2138                 
break;
2139             
default:
2140                 
// Catches case 'plain': and case '':
2141                 $result .= $
this->textLine('Content-Type: ' . $this->ContentType . '; charset=' . $this->CharSet);
2142                 $ismultipart =
false;
2143                 
break;
2144         }
2145         
// RFC1341 part 5 says 7bit is assumed if not specified
2146         
if ($this->Encoding != '7bit') {
2147             
// RFC 2045 section 6.4 says multipart MIME parts may only use 7bit, 8bit or binary CTE
2148             
if ($ismultipart) {
2149                 
if ($this->Encoding == '8bit') {
2150                     $result .= $
this->headerLine('Content-Transfer-Encoding', '8bit');
2151                 }
2152                 
// The only remaining alternatives are quoted-printable and base64, which are both 7bit compatible
2153             }
else {
2154                 $result .= $
this->headerLine('Content-Transfer-Encoding', $this->Encoding);
2155             }
2156         }
2157
2158         
if ($this->Mailer != 'mail') {
2159             $result .= $
this->LE;
2160         }
2161
2162         
return $result;
2163     }
2164
2165     
/**
2166      * Returns the whole MIME message.
2167      * Includes complete headers and body.
2168      * Only valid post preSend().
2169      * @see PHPMailer::preSend()
2170      * @access
public
2171      * @
return string
2172      */

2173     
public function getSentMIMEMessage()
2174     {
2175         
return rtrim($this->MIMEHeader . $this->mailHeader, "\n\r") . self::CRLF . self::CRLF . $this->MIMEBody;
2176     }
2177
2178     
/**
2179      * Create unique ID
2180      * @
return string
2181      */

2182     
protected function generateId() {
2183         
return md5(uniqid(time()));
2184     }
2185
2186     
/**
2187      * Assemble the message body.
2188      * Returns an empty
string on failure.
2189      * @access
public
2190      * @throws phpmailerException
2191      * @
return string The assembled message body
2192      */

2193     
public function createBody()
2194     {
2195         $body =
'';
2196         
//Create unique IDs and preset boundaries
2197         $
this->uniqueid = $this->generateId();
2198         $
this->boundary[1] = 'b1_' . $this->uniqueid;
2199         $
this->boundary[2] = 'b2_' . $this->uniqueid;
2200         $
this->boundary[3] = 'b3_' . $this->uniqueid;
2201
2202         
if ($this->sign_key_file) {
2203             $body .= $
this->getMailMIME() . $this->LE;
2204         }
2205
2206         $
this->setWordWrap();
2207
2208         $bodyEncoding = $
this->Encoding;
2209         $bodyCharSet = $
this->CharSet;
2210         
//Can we do a 7-bit downgrade?
2211         
if ($bodyEncoding == '8bit' and !$this->has8bitChars($this->Body)) {
2212             $bodyEncoding =
'7bit';
2213             
//All ISO 8859, Windows codepage and UTF-8 charsets are ascii compatible up to 7-bit
2214             $bodyCharSet =
'us-ascii';
2215         }
2216         
//If lines are too long, and we're not already using an encoding that will shorten them,
2217         
//change to quoted-printable transfer encoding for the body part only
2218         
if ('base64' != $this->Encoding and self::hasLineLongerThanMax($this->Body)) {
2219             $bodyEncoding =
'quoted-printable';
2220         }
2221
2222         $altBodyEncoding = $
this->Encoding;
2223         $altBodyCharSet = $
this->CharSet;
2224         
//Can we do a 7-bit downgrade?
2225         
if ($altBodyEncoding == '8bit' and !$this->has8bitChars($this->AltBody)) {
2226             $altBodyEncoding =
'7bit';
2227             
//All ISO 8859, Windows codepage and UTF-8 charsets are ascii compatible up to 7-bit
2228             $altBodyCharSet =
'us-ascii';
2229         }
2230         
//If lines are too long, and we're not already using an encoding that will shorten them,
2231         
//change to quoted-printable transfer encoding for the alt body part only
2232         
if ('base64' != $altBodyEncoding and self::hasLineLongerThanMax($this->AltBody)) {
2233             $altBodyEncoding =
'quoted-printable';
2234         }
2235         
//Use this as a preamble in all multipart message types
2236         $mimepre =
"This is a multi-part message in MIME format." . $this->LE . $this->LE;
2237         
switch ($this->message_type) {
2238             
case 'inline':
2239                 $body .= $mimepre;
2240                 $body .= $
this->getBoundary($this->boundary[1], $bodyCharSet, '', $bodyEncoding);
2241                 $body .= $
this->encodeString($this->Body, $bodyEncoding);
2242                 $body .= $
this->LE . $this->LE;
2243                 $body .= $
this->attachAll('inline', $this->boundary[1]);
2244                 
break;
2245             
case 'attach':
2246                 $body .= $mimepre;
2247                 $body .= $
this->getBoundary($this->boundary[1], $bodyCharSet, '', $bodyEncoding);
2248                 $body .= $
this->encodeString($this->Body, $bodyEncoding);
2249                 $body .= $
this->LE . $this->LE;
2250                 $body .= $
this->attachAll('attachment', $this->boundary[1]);
2251                 
break;
2252             
case 'inline_attach':
2253                 $body .= $mimepre;
2254                 $body .= $
this->textLine('--' . $this->boundary[1]);
2255                 $body .= $
this->headerLine('Content-Type', 'multipart/related;');
2256                 $body .= $
this->textLine("\tboundary=\"" . $this->boundary[2] . '"');
2257                 $body .= $
this->LE;
2258                 $body .= $
this->getBoundary($this->boundary[2], $bodyCharSet, '', $bodyEncoding);
2259                 $body .= $
this->encodeString($this->Body, $bodyEncoding);
2260                 $body .= $
this->LE . $this->LE;
2261                 $body .= $
this->attachAll('inline', $this->boundary[2]);
2262                 $body .= $
this->LE;
2263                 $body .= $
this->attachAll('attachment', $this->boundary[1]);
2264                 
break;
2265             
case 'alt':
2266                 $body .= $mimepre;
2267                 $body .= $
this->getBoundary($this->boundary[1], $altBodyCharSet, 'text/plain', $altBodyEncoding);
2268                 $body .= $
this->encodeString($this->AltBody, $altBodyEncoding);
2269                 $body .= $
this->LE . $this->LE;
2270                 $body .= $
this->getBoundary($this->boundary[1], $bodyCharSet, 'text/html', $bodyEncoding);
2271                 $body .= $
this->encodeString($this->Body, $bodyEncoding);
2272                 $body .= $
this->LE . $this->LE;
2273                 
if (!empty($this->Ical)) {
2274                     $body .= $
this->getBoundary($this->boundary[1], '', 'text/calendar; method=REQUEST', '');
2275                     $body .= $
this->encodeString($this->Ical, $this->Encoding);
2276                     $body .= $
this->LE . $this->LE;
2277                 }
2278                 $body .= $
this->endBoundary($this->boundary[1]);
2279                 
break;
2280             
case 'alt_inline':
2281                 $body .= $mimepre;
2282                 $body .= $
this->getBoundary($this->boundary[1], $altBodyCharSet, 'text/plain', $altBodyEncoding);
2283                 $body .= $
this->encodeString($this->AltBody, $altBodyEncoding);
2284                 $body .= $
this->LE . $this->LE;
2285                 $body .= $
this->textLine('--' . $this->boundary[1]);
2286                 $body .= $
this->headerLine('Content-Type', 'multipart/related;');
2287                 $body .= $
this->textLine("\tboundary=\"" . $this->boundary[2] . '"');
2288                 $body .= $
this->LE;
2289                 $body .= $
this->getBoundary($this->boundary[2], $bodyCharSet, 'text/html', $bodyEncoding);
2290                 $body .= $
this->encodeString($this->Body, $bodyEncoding);
2291                 $body .= $
this->LE . $this->LE;
2292                 $body .= $
this->attachAll('inline', $this->boundary[2]);
2293                 $body .= $
this->LE;
2294                 $body .= $
this->endBoundary($this->boundary[1]);
2295                 
break;
2296             
case 'alt_attach':
2297                 $body .= $mimepre;
2298                 $body .= $
this->textLine('--' . $this->boundary[1]);
2299                 $body .= $
this->headerLine('Content-Type', 'multipart/alternative;');
2300                 $body .= $
this->textLine("\tboundary=\"" . $this->boundary[2] . '"');
2301                 $body .= $
this->LE;
2302                 $body .= $
this->getBoundary($this->boundary[2], $altBodyCharSet, 'text/plain', $altBodyEncoding);
2303                 $body .= $
this->encodeString($this->AltBody, $altBodyEncoding);
2304                 $body .= $
this->LE . $this->LE;
2305                 $body .= $
this->getBoundary($this->boundary[2], $bodyCharSet, 'text/html', $bodyEncoding);
2306                 $body .= $
this->encodeString($this->Body, $bodyEncoding);
2307                 $body .= $
this->LE . $this->LE;
2308                 $body .= $
this->endBoundary($this->boundary[2]);
2309                 $body .= $
this->LE;
2310                 $body .= $
this->attachAll('attachment', $this->boundary[1]);
2311                 
break;
2312             
case 'alt_inline_attach':
2313                 $body .= $mimepre;
2314                 $body .= $
this->textLine('--' . $this->boundary[1]);
2315                 $body .= $
this->headerLine('Content-Type', 'multipart/alternative;');
2316                 $body .= $
this->textLine("\tboundary=\"" . $this->boundary[2] . '"');
2317                 $body .= $
this->LE;
2318                 $body .= $
this->getBoundary($this->boundary[2], $altBodyCharSet, 'text/plain', $altBodyEncoding);
2319                 $body .= $
this->encodeString($this->AltBody, $altBodyEncoding);
2320                 $body .= $
this->LE . $this->LE;
2321                 $body .= $
this->textLine('--' . $this->boundary[2]);
2322                 $body .= $
this->headerLine('Content-Type', 'multipart/related;');
2323                 $body .= $
this->textLine("\tboundary=\"" . $this->boundary[3] . '"');
2324                 $body .= $
this->LE;
2325                 $body .= $
this->getBoundary($this->boundary[3], $bodyCharSet, 'text/html', $bodyEncoding);
2326                 $body .= $
this->encodeString($this->Body, $bodyEncoding);
2327                 $body .= $
this->LE . $this->LE;
2328                 $body .= $
this->attachAll('inline', $this->boundary[3]);
2329                 $body .= $
this->LE;
2330                 $body .= $
this->endBoundary($this->boundary[2]);
2331                 $body .= $
this->LE;
2332                 $body .= $
this->attachAll('attachment', $this->boundary[1]);
2333                 
break;
2334             
default:
2335                 
// Catch case 'plain' and case '', applies to simple `text/plain` and `text/html` body content types
2336                 
//Reset the `Encoding` property in case we changed it for line length reasons
2337                 $
this->Encoding = $bodyEncoding;
2338                 $body .= $
this->encodeString($this->Body, $this->Encoding);
2339                 
break;
2340         }
2341
2342         
if ($this->isError()) {
2343             $body =
'';
2344         } elseif ($
this->sign_key_file) {
2345             
try {
2346                 
if (!defined('PKCS7_TEXT')) {
2347                     
throw new phpmailerException($this->lang('extension_missing') . 'openssl');
2348                 }
2349                 
// @TODO would be nice to use php://temp streams here, but need to wrap for PHP < 5.1
2350                 $file = tempnam(sys_get_temp_dir(),
'mail');
2351                 
if (false === file_put_contents($file, $body)) {
2352                     
throw new phpmailerException($this->lang('signing') . ' Could not write temp file');
2353                 }
2354                 $signed = tempnam(sys_get_temp_dir(),
'signed');
2355                 
//Workaround for PHP bug https://bugs.php.net/bug.php?id=69197
2356                 
if (empty($this->sign_extracerts_file)) {
2357                     $sign = @openssl_pkcs7_sign(
2358                         $file,
2359                         $signed,
2360                         
'file://' . realpath($this->sign_cert_file),
2361                         array(
'file://' . realpath($this->sign_key_file), $this->sign_key_pass),
2362                         
null
2363                     );
2364                 }
else {
2365                     $sign = @openssl_pkcs7_sign(
2366                         $file,
2367                         $signed,
2368                         
'file://' . realpath($this->sign_cert_file),
2369                         array(
'file://' . realpath($this->sign_key_file), $this->sign_key_pass),
2370                         
null,
2371                         PKCS7_DETACHED,
2372                         $
this->sign_extracerts_file
2373                     );
2374                 }
2375                 
if ($sign) {
2376                     @unlink($file);
2377                     $body = file_get_contents($signed);
2378                     @unlink($signed);
2379                     
//The message returned by openssl contains both headers and body, so need to split them up
2380                     $parts = explode(
"\n\n", $body, 2);
2381                     $
this->MIMEHeader .= $parts[0] . $this->LE . $this->LE;
2382                     $body = $parts[
1];
2383                 }
else {
2384                     @unlink($file);
2385                     @unlink($signed);
2386                     
throw new phpmailerException($this->lang('signing') . openssl_error_string());
2387                 }
2388             }
catch (phpmailerException $exc) {
2389                 $body =
'';
2390                 
if ($this->exceptions) {
2391                     
throw $exc;
2392                 }
2393             }
2394         }
2395         
return $body;
2396     }
2397
2398     
/**
2399      * Return the start of a message boundary.
2400      * @access
protected
2401      * @param
string $boundary
2402      * @param
string $charSet
2403      * @param
string $contentType
2404      * @param
string $encoding
2405      * @
return string
2406      */

2407     
protected function getBoundary($boundary, $charSet, $contentType, $encoding)
2408     {
2409         $result =
'';
2410         
if ($charSet == '') {
2411             $charSet = $
this->CharSet;
2412         }
2413         
if ($contentType == '') {
2414             $contentType = $
this->ContentType;
2415         }
2416         
if ($encoding == '') {
2417             $encoding = $
this->Encoding;
2418         }
2419         $result .= $
this->textLine('--' . $boundary);
2420         $result .= sprintf(
'Content-Type: %s; charset=%s', $contentType, $charSet);
2421         $result .= $
this->LE;
2422         
// RFC1341 part 5 says 7bit is assumed if not specified
2423         
if ($encoding != '7bit') {
2424             $result .= $
this->headerLine('Content-Transfer-Encoding', $encoding);
2425         }
2426         $result .= $
this->LE;
2427
2428         
return $result;
2429     }
2430
2431     
/**
2432      * Return the end of a message boundary.
2433      * @access
protected
2434      * @param
string $boundary
2435      * @
return string
2436      */

2437     
protected function endBoundary($boundary)
2438     {
2439         
return $this->LE . '--' . $boundary . '--' . $this->LE;
2440     }
2441
2442     
/**
2443      * Set the message type.
2444      * PHPMailer only supports some preset message types, not arbitrary MIME structures.
2445      * @access
protected
2446      * @
return void
2447      */

2448     
protected function setMessageType()
2449     {
2450         $type = array();
2451         
if ($this->alternativeExists()) {
2452             $type[] =
'alt';
2453         }
2454         
if ($this->inlineImageExists()) {
2455             $type[] =
'inline';
2456         }
2457         
if ($this->attachmentExists()) {
2458             $type[] =
'attach';
2459         }
2460         $
this->message_type = implode('_', $type);
2461         
if ($this->message_type == '') {
2462             
//The 'plain' message_type refers to the message having a single body element, not that it is plain-text
2463             $
this->message_type = 'plain';
2464         }
2465     }
2466
2467     
/**
2468      * Format a header line.
2469      * @access
public
2470      * @param
string $name
2471      * @param
string $value
2472      * @
return string
2473      */

2474     
public function headerLine($name, $value)
2475     {
2476         
return $name . ': ' . $value . $this->LE;
2477     }
2478
2479     
/**
2480      * Return a formatted mail line.
2481      * @access
public
2482      * @param
string $value
2483      * @
return string
2484      */

2485     
public function textLine($value)
2486     {
2487         
return $value . $this->LE;
2488     }
2489
2490     
/**
2491      * Add an attachment
from a path on the filesystem.
2492      * Never use a user-supplied path to a file!
2493      * Returns
false if the file could not be found or read.
2494      * @param
string $path Path to the attachment.
2495      * @param
string $name Overrides the attachment name.
2496      * @param
string $encoding File encoding (see $Encoding).
2497      * @param
string $type File extension (MIME) type.
2498      * @param
string $disposition Disposition to use
2499      * @throws phpmailerException
2500      * @
return boolean
2501      */

2502     
public function addAttachment($path, $name = '', $encoding = 'base64', $type = '', $disposition = 'attachment')
2503     {
2504         
try {
2505             
if (!@is_file($path)) {
2506                 
throw new phpmailerException($this->lang('file_access') . $path, self::STOP_CONTINUE);
2507             }
2508
2509             
// If a MIME type is not specified, try to work it out from the file name
2510             
if ($type == '') {
2511                 $type = self::filenameToType($path);
2512             }
2513
2514             $filename = basename($path);
2515             
if ($name == '') {
2516                 $name = $filename;
2517             }
2518
2519             $
this->attachment[] = array(
2520                 
0 => $path,
2521                 
1 => $filename,
2522                 
2 => $name,
2523                 
3 => $encoding,
2524                 
4 => $type,
2525                 
5 => false, // isStringAttachment
2526                 
6 => $disposition,
2527                 
7 => 0
2528             );
2529
2530         }
catch (phpmailerException $exc) {
2531             $
this->setError($exc->getMessage());
2532             $
this->edebug($exc->getMessage());
2533             
if ($this->exceptions) {
2534                 
throw $exc;
2535             }
2536             
return false;
2537         }
2538         
return true;
2539     }
2540
2541     
/**
2542      * Return the array of attachments.
2543      * @
return array
2544      */

2545     
public function getAttachments()
2546     {
2547         
return $this->attachment;
2548     }
2549
2550     
/**
2551      * Attach all file,
string, and binary attachments to the message.
2552      * Returns an empty
string on failure.
2553      * @access
protected
2554      * @param
string $disposition_type
2555      * @param
string $boundary
2556      * @
return string
2557      */

2558     
protected function attachAll($disposition_type, $boundary)
2559     {
2560         
// Return text of body
2561         $mime = array();
2562         $cidUniq = array();
2563         $incl = array();
2564
2565         
// Add all attachments
2566         
foreach ($this->attachment as $attachment) {
2567             
// Check if it is a valid disposition_filter
2568             
if ($attachment[6] == $disposition_type) {
2569                 
// Check for string attachment
2570                 $
string = '';
2571                 $path =
'';
2572                 $bString = $attachment[
5];
2573                 
if ($bString) {
2574                     $
string = $attachment[0];
2575                 }
else {
2576                     $path = $attachment[
0];
2577                 }
2578
2579                 $inclhash = md5(serialize($attachment));
2580                 
if (in_array($inclhash, $incl)) {
2581                     
continue;
2582                 }
2583                 $incl[] = $inclhash;
2584                 $name = $attachment[
2];
2585                 $encoding = $attachment[
3];
2586                 $type = $attachment[
4];
2587                 $disposition = $attachment[
6];
2588                 $cid = $attachment[
7];
2589                 
if ($disposition == 'inline' && array_key_exists($cid, $cidUniq)) {
2590                     
continue;
2591                 }
2592                 $cidUniq[$cid] =
true;
2593
2594                 $mime[] = sprintf(
'--%s%s', $boundary, $this->LE);
2595                 
//Only include a filename property if we have one
2596                 
if (!empty($name)) {
2597                     $mime[] = sprintf(
2598                         
'Content-Type: %s; name="%s"%s',
2599                         $type,
2600                         $
this->encodeHeader($this->secureHeader($name)),
2601                         $
this->LE
2602                     );
2603                 }
else {
2604                     $mime[] = sprintf(
2605                         
'Content-Type: %s%s',
2606                         $type,
2607                         $
this->LE
2608                     );
2609                 }
2610                 
// RFC1341 part 5 says 7bit is assumed if not specified
2611                 
if ($encoding != '7bit') {
2612                     $mime[] = sprintf(
'Content-Transfer-Encoding: %s%s', $encoding, $this->LE);
2613                 }
2614
2615                 
if ($disposition == 'inline') {
2616                     $mime[] = sprintf(
'Content-ID: <%s>%s', $cid, $this->LE);
2617                 }
2618
2619                 
// If a filename contains any of these chars, it should be quoted,
2620                 
// but not otherwise: RFC2183 & RFC2045 5.1
2621                 
// Fixes a warning in IETF's msglint MIME checker
2622                 
// Allow for bypassing the Content-Disposition header totally
2623                 
if (!(empty($disposition))) {
2624                     $encoded_name = $
this->encodeHeader($this->secureHeader($name));
2625                     
if (preg_match('/[ \(\)<>@,;:\\"\/\[\]\?=]/', $encoded_name)) {
2626                         $mime[] = sprintf(
2627                             
'Content-Disposition: %s; filename="%s"%s',
2628                             $disposition,
2629                             $encoded_name,
2630                             $
this->LE . $this->LE
2631                         );
2632                     }
else {
2633                         
if (!empty($encoded_name)) {
2634                             $mime[] = sprintf(
2635                                 
'Content-Disposition: %s; filename=%s%s',
2636                                 $disposition,
2637                                 $encoded_name,
2638                                 $
this->LE . $this->LE
2639                             );
2640                         }
else {
2641                             $mime[] = sprintf(
2642                                 
'Content-Disposition: %s%s',
2643                                 $disposition,
2644                                 $
this->LE . $this->LE
2645                             );
2646                         }
2647                     }
2648                 }
else {
2649                     $mime[] = $
this->LE;
2650                 }
2651
2652                 
// Encode as string attachment
2653                 
if ($bString) {
2654                     $mime[] = $
this->encodeString($string, $encoding);
2655                     
if ($this->isError()) {
2656                         
return '';
2657                     }
2658                     $mime[] = $
this->LE . $this->LE;
2659                 }
else {
2660                     $mime[] = $
this->encodeFile($path, $encoding);
2661                     
if ($this->isError()) {
2662                         
return '';
2663                     }
2664                     $mime[] = $
this->LE . $this->LE;
2665                 }
2666             }
2667         }
2668
2669         $mime[] = sprintf(
'--%s--%s', $boundary, $this->LE);
2670
2671         
return implode('', $mime);
2672     }
2673
2674     
/**
2675      * Encode a file attachment
in requested format.
2676      * Returns an empty
string on failure.
2677      * @param
string $path The full path to the file
2678      * @param
string $encoding The encoding to use; one of 'base64', '7bit', '8bit', 'binary', 'quoted-printable'
2679      * @throws phpmailerException
2680      * @access
protected
2681      * @
return string
2682      */

2683     
protected function encodeFile($path, $encoding = 'base64')
2684     {
2685         
try {
2686             
if (!is_readable($path)) {
2687                 
throw new phpmailerException($this->lang('file_open') . $path, self::STOP_CONTINUE);
2688             }
2689             $magic_quotes = get_magic_quotes_runtime();
2690             
if ($magic_quotes) {
2691                 
if (version_compare(PHP_VERSION, '5.3.0', '<')) {
2692                     set_magic_quotes_runtime(
false);
2693                 }
else {
2694                     
//Doesn't exist in PHP 5.4, but we don't need to check because
2695                     
//get_magic_quotes_runtime always returns false in 5.4+
2696                     
//so it will never get here
2697                     ini_set(
'magic_quotes_runtime', false);
2698                 }
2699             }
2700             $file_buffer = file_get_contents($path);
2701             $file_buffer = $
this->encodeString($file_buffer, $encoding);
2702             
if ($magic_quotes) {
2703                 
if (version_compare(PHP_VERSION, '5.3.0', '<')) {
2704                     set_magic_quotes_runtime($magic_quotes);
2705                 }
else {
2706                     ini_set(
'magic_quotes_runtime', $magic_quotes);
2707                 }
2708             }
2709             
return $file_buffer;
2710         }
catch (Exception $exc) {
2711             $
this->setError($exc->getMessage());
2712             
return '';
2713         }
2714     }
2715
2716     
/**
2717      * Encode a
string in requested format.
2718      * Returns an empty
string on failure.
2719      * @param
string $str The text to encode
2720      * @param
string $encoding The encoding to use; one of 'base64', '7bit', '8bit', 'binary', 'quoted-printable'
2721      * @access
public
2722      * @
return string
2723      */

2724     
public function encodeString($str, $encoding = 'base64')
2725     {
2726         $encoded =
'';
2727         
switch (strtolower($encoding)) {
2728             
case 'base64':
2729                 $encoded = chunk_split(base64_encode($str),
76, $this->LE);
2730                 
break;
2731             
case '7bit':
2732             
case '8bit':
2733                 $encoded = $
this->fixEOL($str);
2734                 
// Make sure it ends with a line break
2735                 
if (substr($encoded, -(strlen($this->LE))) != $this->LE) {
2736                     $encoded .= $
this->LE;
2737                 }
2738                 
break;
2739             
case 'binary':
2740                 $encoded = $str;
2741                 
break;
2742             
case 'quoted-printable':
2743                 $encoded = $
this->encodeQP($str);
2744                 
break;
2745             
default:
2746                 $
this->setError($this->lang('encoding') . $encoding);
2747                 
break;
2748         }
2749         
return $encoded;
2750     }
2751
2752     
/**
2753      * Encode a header
string optimally.
2754      * Picks shortest of Q, B, quoted-printable or none.
2755      * @access
public
2756      * @param
string $str
2757      * @param
string $position
2758      * @
return string
2759      */

2760     
public function encodeHeader($str, $position = 'text')
2761     {
2762         $matchcount =
0;
2763         
switch (strtolower($position)) {
2764             
case 'phrase':
2765                 
if (!preg_match('/[\200-\377]/', $str)) {
2766                     
// Can't use addslashes as we don't know the value of magic_quotes_sybase
2767                     $encoded = addcslashes($str,
"\0..\37\177\\\"");
2768                     
if (($str == $encoded) && !preg_match('/[^A-Za-z0-9!#$%&\'*+\/=?^_`{|}~ -]/', $str)) {
2769                         
return ($encoded);
2770                     }
else {
2771                         
return ("\"$encoded\"");
2772                     }
2773                 }
2774                 $matchcount = preg_match_all(
'/[^\040\041\043-\133\135-\176]/', $str, $matches);
2775                 
break;
2776             
/** @noinspection PhpMissingBreakStatementInspection */
2777             
case 'comment':
2778                 $matchcount = preg_match_all(
'/[()"]/', $str, $matches);
2779                 
// Intentional fall-through
2780             
case 'text':
2781             
default:
2782                 $matchcount += preg_match_all(
'/[\000-\010\013\014\016-\037\177-\377]/', $str, $matches);
2783                 
break;
2784         }
2785
2786         
//There are no chars that need encoding
2787         
if ($matchcount == 0) {
2788             
return ($str);
2789         }
2790
2791         $maxlen =
75 - 7 - strlen($this->CharSet);
2792         
// Try to select the encoding which should produce the shortest output
2793         
if ($matchcount > strlen($str) / 3) {
2794             
// More than a third of the content will need encoding, so B encoding will be most efficient
2795             $encoding =
'B';
2796             
if (function_exists('mb_strlen') && $this->hasMultiBytes($str)) {
2797                 
// Use a custom function which correctly encodes and wraps long
2798                 
// multibyte strings without breaking lines within a character
2799                 $encoded = $
this->base64EncodeWrapMB($str, "\n");
2800             }
else {
2801                 $encoded = base64_encode($str);
2802                 $maxlen -= $maxlen %
4;
2803                 $encoded = trim(chunk_split($encoded, $maxlen,
"\n"));
2804             }
2805         }
else {
2806             $encoding =
'Q';
2807             $encoded = $
this->encodeQ($str, $position);
2808             $encoded = $
this->wrapText($encoded, $maxlen, true);
2809             $encoded = str_replace(
'=' . self::CRLF, "\n", trim($encoded));
2810         }
2811
2812         $encoded = preg_replace(
'/^(.*)$/m', ' =?' . $this->CharSet . "?$encoding?\\1?=", $encoded);
2813         $encoded = trim(str_replace(
"\n", $this->LE, $encoded));
2814
2815         
return $encoded;
2816     }
2817
2818     
/**
2819      * Check
if a string contains multi-byte characters.
2820      * @access
public
2821      * @param
string $str multi-byte text to wrap encode
2822      * @
return boolean
2823      */

2824     
public function hasMultiBytes($str)
2825     {
2826         
if (function_exists('mb_strlen')) {
2827             
return (strlen($str) > mb_strlen($str, $this->CharSet));
2828         }
else { // Assume no multibytes (we can't handle without mbstring functions anyway)
2829             
return false;
2830         }
2831     }
2832
2833     
/**
2834      * Does a
string contain any 8-bit chars (in any charset)?
2835      * @param
string $text
2836      * @
return boolean
2837      */

2838     
public function has8bitChars($text)
2839     {
2840         
return (boolean)preg_match('/[\x80-\xFF]/', $text);
2841     }
2842
2843     
/**
2844      * Encode and wrap
long multibyte strings for mail headers
2845      * without breaking lines within a character.
2846      * Adapted
from a function by paravoid
2847      * @link http://www.php.net/manual/en/function.mb-encode-mimeheader.php#
60283
2848      * @access
public
2849      * @param
string $str multi-byte text to wrap encode
2850      * @param
string $linebreak string to use as linefeed/end-of-line
2851      * @
return string
2852      */

2853     
public function base64EncodeWrapMB($str, $linebreak = null)
2854     {
2855         $start =
'=?' . $this->CharSet . '?B?';
2856         $end =
'?=';
2857         $encoded =
'';
2858         
if ($linebreak === null) {
2859             $linebreak = $
this->LE;
2860         }
2861
2862         $mb_length = mb_strlen($str, $
this->CharSet);
2863         
// Each line must have length <= 75, including $start and $end
2864         $length =
75 - strlen($start) - strlen($end);
2865         
// Average multi-byte ratio
2866         $ratio = $mb_length / strlen($str);
2867         
// Base64 has a 4:3 ratio
2868         $avgLength = floor($length * $ratio * .
75);
2869
2870         
for ($i = 0; $i < $mb_length; $i += $offset) {
2871             $lookBack =
0;
2872             
do {
2873                 $offset = $avgLength - $lookBack;
2874                 $chunk = mb_substr($str, $i, $offset, $
this->CharSet);
2875                 $chunk = base64_encode($chunk);
2876                 $lookBack++;
2877             }
while (strlen($chunk) > $length);
2878             $encoded .= $chunk . $linebreak;
2879         }
2880
2881         
// Chomp the last linefeed
2882         $encoded = substr($encoded,
0, -strlen($linebreak));
2883         
return $encoded;
2884     }
2885
2886     
/**
2887      * Encode a
string in quoted-printable format.
2888      * According to RFC2045 section
6.7.
2889      * @access
public
2890      * @param
string $string The text to encode
2891      * @param integer $line_max Number of chars allowed
on a line before wrapping
2892      * @
return string
2893      * @link http://www.php.net/manual/en/function.quoted-printable-decode.php#
89417 Adapted from this comment
2894      */

2895     
public function encodeQP($string, $line_max = 76)
2896     {
2897         
// Use native function if it's available (>= PHP5.3)
2898         
if (function_exists('quoted_printable_encode')) {
2899             
return quoted_printable_encode($string);
2900         }
2901         
// Fall back to a pure PHP implementation
2902         $
string = str_replace(
2903             array(
'%20', '%0D%0A.', '%0D%0A', '%'),
2904             array(
' ', "\r\n=2E", "\r\n", '='),
2905             rawurlencode($
string)
2906         );
2907         
return preg_replace('/[^\r\n]{' . ($line_max - 3) . '}[^=\r\n]{2}/', "$0=\r\n", $string);
2908     }
2909
2910     
/**
2911      * Backward compatibility wrapper
for an old QP encoding function that was removed.
2912      * @see PHPMailer::encodeQP()
2913      * @access
public
2914      * @param
string $string
2915      * @param integer $line_max
2916      * @param boolean $space_conv
2917      * @
return string
2918      * @deprecated Use encodeQP instead.
2919      */

2920     
public function encodeQPphp(
2921         $
string,
2922         $line_max =
76,
2923         
/** @noinspection PhpUnusedParameterInspection */ $space_conv = false
2924     ) {
2925         
return $this->encodeQP($string, $line_max);
2926     }
2927
2928     
/**
2929      * Encode a
string using Q encoding.
2930      * @link http://tools.ietf.org/html/rfc2047
2931      * @param
string $str the text to encode
2932      * @param
string $position Where the text is going to be used, see the RFC for what that means
2933      * @access
public
2934      * @
return string
2935      */

2936     
public function encodeQ($str, $position = 'text')
2937     {
2938         
// There should not be any EOL in the string
2939         $pattern =
'';
2940         $encoded = str_replace(array(
"\r", "\n"), '', $str);
2941         
switch (strtolower($position)) {
2942             
case 'phrase':
2943                 
// RFC 2047 section 5.3
2944                 $pattern =
'^A-Za-z0-9!*+\/ -';
2945                 
break;
2946             
/** @noinspection PhpMissingBreakStatementInspection */
2947             
case 'comment':
2948                 
// RFC 2047 section 5.2
2949                 $pattern =
'\(\)"';
2950                 
// intentional fall-through
2951                 
// for this reason we build the $pattern without including delimiters and []
2952             
case 'text':
2953             
default:
2954                 
// RFC 2047 section 5.1
2955                 
// Replace every high ascii, control, =, ? and _ characters
2956                 $pattern =
'\000-\011\013\014\016-\037\075\077\137\177-\377' . $pattern;
2957                 
break;
2958         }
2959         $matches = array();
2960         
if (preg_match_all("/[{$pattern}]/", $encoded, $matches)) {
2961             
// If the string contains an '=', make sure it's the first thing we replace
2962             
// so as to avoid double-encoding
2963             $eqkey = array_search(
'=', $matches[0]);
2964             
if (false !== $eqkey) {
2965                 unset($matches[
0][$eqkey]);
2966                 array_unshift($matches[
0], '=');
2967             }
2968             
foreach (array_unique($matches[0]) as $char) {
2969                 $encoded = str_replace($
char, '=' . sprintf('%02X', ord($char)), $encoded);
2970             }
2971         }
2972         
// Replace every spaces to _ (more readable than =20)
2973         
return str_replace(' ', '_', $encoded);
2974     }
2975
2976     
/**
2977      * Add a
string or binary attachment (non-filesystem).
2978      * This method can be used to attach ascii or binary data,
2979      * such
as a BLOB record from a database.
2980      * @param
string $string String attachment data.
2981      * @param
string $filename Name of the attachment.
2982      * @param
string $encoding File encoding (see $Encoding).
2983      * @param
string $type File extension (MIME) type.
2984      * @param
string $disposition Disposition to use
2985      * @
return void
2986      */

2987     
public function addStringAttachment(
2988         $
string,
2989         $filename,
2990         $encoding =
'base64',
2991         $type =
'',
2992         $disposition =
'attachment'
2993     ) {
2994         
// If a MIME type is not specified, try to work it out from the file name
2995         
if ($type == '') {
2996             $type = self::filenameToType($filename);
2997         }
2998         
// Append to $attachment array
2999         $
this->attachment[] = array(
3000             
0 => $string,
3001             
1 => $filename,
3002             
2 => basename($filename),
3003             
3 => $encoding,
3004             
4 => $type,
3005             
5 => true, // isStringAttachment
3006             
6 => $disposition,
3007             
7 => 0
3008         );
3009     }
3010
3011     
/**
3012      * Add an embedded (inline) attachment
from a file.
3013      * This can include images, sounds, and just about any other document type.
3014      * These differ
from 'regular' attachments in that they are intended to be
3015      * displayed inline with the message, not just attached
for download.
3016      * This
is used in HTML messages that embed the images
3017      * the HTML refers to
using the $cid value.
3018      * Never use a user-supplied path to a file!
3019      * @param
string $path Path to the attachment.
3020      * @param
string $cid Content ID of the attachment; Use this to reference
3021      * the content
when using an embedded image in HTML.
3022      * @param
string $name Overrides the attachment name.
3023      * @param
string $encoding File encoding (see $Encoding).
3024      * @param
string $type File MIME type.
3025      * @param
string $disposition Disposition to use
3026      * @
return boolean True on successfully adding an attachment
3027      */

3028     
public function addEmbeddedImage($path, $cid, $name = '', $encoding = 'base64', $type = '', $disposition = 'inline')
3029     {
3030         
if (!@is_file($path)) {
3031             $
this->setError($this->lang('file_access') . $path);
3032             
return false;
3033         }
3034
3035         
// If a MIME type is not specified, try to work it out from the file name
3036         
if ($type == '') {
3037             $type = self::filenameToType($path);
3038         }
3039
3040         $filename = basename($path);
3041         
if ($name == '') {
3042             $name = $filename;
3043         }
3044
3045         
// Append to $attachment array
3046         $
this->attachment[] = array(
3047             
0 => $path,
3048             
1 => $filename,
3049             
2 => $name,
3050             
3 => $encoding,
3051             
4 => $type,
3052             
5 => false, // isStringAttachment
3053             
6 => $disposition,
3054             
7 => $cid
3055         );
3056         
return true;
3057     }
3058
3059     
/**
3060      * Add an embedded stringified attachment.
3061      * This can include images, sounds, and just about any other document type.
3062      * Be sure to
set the $type to an image type for images:
3063      * JPEG images use
'image/jpeg', GIF uses 'image/gif', PNG uses 'image/png'.
3064      * @param
string $string The attachment binary data.
3065      * @param
string $cid Content ID of the attachment; Use this to reference
3066      * the content
when using an embedded image in HTML.
3067      * @param
string $name
3068      * @param
string $encoding File encoding (see $Encoding).
3069      * @param
string $type MIME type.
3070      * @param
string $disposition Disposition to use
3071      * @
return boolean True on successfully adding an attachment
3072      */

3073     
public function addStringEmbeddedImage(
3074         $
string,
3075         $cid,
3076         $name =
'',
3077         $encoding =
'base64',
3078         $type =
'',
3079         $disposition =
'inline'
3080     ) {
3081         
// If a MIME type is not specified, try to work it out from the name
3082         
if ($type == '' and !empty($name)) {
3083             $type = self::filenameToType($name);
3084         }
3085
3086         
// Append to $attachment array
3087         $
this->attachment[] = array(
3088             
0 => $string,
3089             
1 => $name,
3090             
2 => $name,
3091             
3 => $encoding,
3092             
4 => $type,
3093             
5 => true, // isStringAttachment
3094             
6 => $disposition,
3095             
7 => $cid
3096         );
3097         
return true;
3098     }
3099
3100     
/**
3101      * Check
if an inline attachment is present.
3102      * @access
public
3103      * @
return boolean
3104      */

3105     
public function inlineImageExists()
3106     {
3107         
foreach ($this->attachment as $attachment) {
3108             
if ($attachment[6] == 'inline') {
3109                 
return true;
3110             }
3111         }
3112         
return false;
3113     }
3114
3115     
/**
3116      * Check
if an attachment (non-inline) is present.
3117      * @
return boolean
3118      */

3119     
public function attachmentExists()
3120     {
3121         
foreach ($this->attachment as $attachment) {
3122             
if ($attachment[6] == 'attachment') {
3123                 
return true;
3124             }
3125         }
3126         
return false;
3127     }
3128
3129     
/**
3130      * Check
if this message has an alternative body set.
3131      * @
return boolean
3132      */

3133     
public function alternativeExists()
3134     {
3135         
return !empty($this->AltBody);
3136     }
3137
3138     
/**
3139      * Clear queued addresses of given kind.
3140      * @access
protected
3141      * @param
string $kind 'to', 'cc', or 'bcc'
3142      * @
return void
3143      */

3144     
public function clearQueuedAddresses($kind)
3145     {
3146         $RecipientsQueue = $
this->RecipientsQueue;
3147         
foreach ($RecipientsQueue as $address => $params) {
3148             
if ($params[0] == $kind) {
3149                 unset($
this->RecipientsQueue[$address]);
3150             }
3151         }
3152     }
3153
3154     
/**
3155      * Clear all To recipients.
3156      * @
return void
3157      */

3158     
public function clearAddresses()
3159     {
3160         
foreach ($this->to as $to) {
3161             unset($
this->all_recipients[strtolower($to[0])]);
3162         }
3163         $
this->to = array();
3164         $
this->clearQueuedAddresses('to');
3165     }
3166
3167     
/**
3168      * Clear all CC recipients.
3169      * @
return void
3170      */

3171     
public function clearCCs()
3172     {
3173         
foreach ($this->cc as $cc) {
3174             unset($
this->all_recipients[strtolower($cc[0])]);
3175         }
3176         $
this->cc = array();
3177         $
this->clearQueuedAddresses('cc');
3178     }
3179
3180     
/**
3181      * Clear all BCC recipients.
3182      * @
return void
3183      */

3184     
public function clearBCCs()
3185     {
3186         
foreach ($this->bcc as $bcc) {
3187             unset($
this->all_recipients[strtolower($bcc[0])]);
3188         }
3189         $
this->bcc = array();
3190         $
this->clearQueuedAddresses('bcc');
3191     }
3192
3193     
/**
3194      * Clear all ReplyTo recipients.
3195      * @
return void
3196      */

3197     
public function clearReplyTos()
3198     {
3199         $
this->ReplyTo = array();
3200         $
this->ReplyToQueue = array();
3201     }
3202
3203     
/**
3204      * Clear all recipient types.
3205      * @
return void
3206      */

3207     
public function clearAllRecipients()
3208     {
3209         $
this->to = array();
3210         $
this->cc = array();
3211         $
this->bcc = array();
3212         $
this->all_recipients = array();
3213         $
this->RecipientsQueue = array();
3214     }
3215
3216     
/**
3217      * Clear all filesystem,
string, and binary attachments.
3218      * @
return void
3219      */

3220     
public function clearAttachments()
3221     {
3222         $
this->attachment = array();
3223     }
3224
3225     
/**
3226      * Clear all custom headers.
3227      * @
return void
3228      */

3229     
public function clearCustomHeaders()
3230     {
3231         $
this->CustomHeader = array();
3232     }
3233
3234     
/**
3235      * Add an error message to the error container.
3236      * @access
protected
3237      * @param
string $msg
3238      * @
return void
3239      */

3240     
protected function setError($msg)
3241     {
3242         $
this->error_count++;
3243         
if ($this->Mailer == 'smtp' and !is_null($this->smtp)) {
3244             $lasterror = $
this->smtp->getError();
3245             
if (!empty($lasterror['error'])) {
3246                 $msg .= $
this->lang('smtp_error') . $lasterror['error'];
3247                 
if (!empty($lasterror['detail'])) {
3248                     $msg .=
' Detail: '. $lasterror['detail'];
3249                 }
3250                 
if (!empty($lasterror['smtp_code'])) {
3251                     $msg .=
' SMTP code: ' . $lasterror['smtp_code'];
3252                 }
3253                 
if (!empty($lasterror['smtp_code_ex'])) {
3254                     $msg .=
' Additional SMTP info: ' . $lasterror['smtp_code_ex'];
3255                 }
3256             }
3257         }
3258         $
this->ErrorInfo = $msg;
3259     }
3260
3261     
/**
3262      * Return an RFC
822 formatted date.
3263      * @access
public
3264      * @
return string
3265      * @
static
3266      */

3267     
public static function rfcDate()
3268     {
3269         
// Set the time zone to whatever the default is to avoid 500 errors
3270         
// Will default to UTC if it's not set properly in php.ini
3271         date_default_timezone_set(@date_default_timezone_get());
3272         
return date('D, j M Y H:i:s O');
3273     }
3274
3275     
/**
3276      * Get the server hostname.
3277      * Returns
'localhost.localdomain' if unknown.
3278      * @access
protected
3279      * @
return string
3280      */

3281     
protected function serverHostname()
3282     {
3283         $result =
'localhost.localdomain';
3284         
if (!empty($this->Hostname)) {
3285             $result = $
this->Hostname;
3286         } elseif (isset($_SERVER) and array_key_exists(
'SERVER_NAME', $_SERVER) and !empty($_SERVER['SERVER_NAME'])) {
3287             $result = $_SERVER[
'SERVER_NAME'];
3288         } elseif (function_exists(
'gethostname') && gethostname() !== false) {
3289             $result = gethostname();
3290         } elseif (php_uname(
'n') !== false) {
3291             $result = php_uname(
'n');
3292         }
3293         
return $result;
3294     }
3295
3296     
/**
3297      * Get an error message
in the current language.
3298      * @access
protected
3299      * @param
string $key
3300      * @
return string
3301      */

3302     
protected function lang($key)
3303     {
3304         
if (count($this->language) < 1) {
3305             $
this->setLanguage('en'); // set the default language
3306         }
3307
3308         
if (array_key_exists($key, $this->language)) {
3309             
if ($key == 'smtp_connect_failed') {
3310                 
//Include a link to troubleshooting docs on SMTP connection failure
3311                 
//this is by far the biggest cause of support questions
3312                 
//but it's usually not PHPMailer's fault.
3313                 
return $this->language[$key] . ' https://github.com/PHPMailer/PHPMailer/wiki/Troubleshooting';
3314             }
3315             
return $this->language[$key];
3316         }
else {
3317             
//Return the key as a fallback
3318             
return $key;
3319         }
3320     }
3321
3322     
/**
3323      * Check
if an error occurred.
3324      * @access
public
3325      * @
return boolean True if an error did occur.
3326      */

3327     
public function isError()
3328     {
3329         
return ($this->error_count > 0);
3330     }
3331
3332     
/**
3333      * Ensure consistent line endings
in a string.
3334      * Changes every end of line
from CRLF, CR or LF to $this->LE.
3335      * @access
public
3336      * @param
string $str String to fixEOL
3337      * @
return string
3338      */

3339     
public function fixEOL($str)
3340     {
3341         
// Normalise to \n
3342         $nstr = str_replace(array(
"\r\n", "\r"), "\n", $str);
3343         
// Now convert LE as needed
3344         
if ($this->LE !== "\n") {
3345             $nstr = str_replace(
"\n", $this->LE, $nstr);
3346         }
3347         
return $nstr;
3348     }
3349
3350     
/**
3351      * Add a custom header.
3352      * $name
value can be overloaded to contain
3353      * both header name and
value (name:value)
3354      * @access
public
3355      * @param
string $name Custom header name
3356      * @param
string $value Header value
3357      * @
return void
3358      */

3359     
public function addCustomHeader($name, $value = null)
3360     {
3361         
if ($value === null) {
3362             
// Value passed in as name:value
3363             $
this->CustomHeader[] = explode(':', $name, 2);
3364         }
else {
3365             $
this->CustomHeader[] = array($name, $value);
3366         }
3367     }
3368
3369     
/**
3370      * Returns all custom headers.
3371      * @
return array
3372      */

3373     
public function getCustomHeaders()
3374     {
3375         
return $this->CustomHeader;
3376     }
3377
3378     
/**
3379      * Create a message body
from an HTML string.
3380      * Automatically inlines images and creates a plain-text version
by converting the HTML,
3381      * overwriting any existing values
in Body and AltBody.
3382      * Do not source $message content
from user input!
3383      * $basedir
is prepended when handling relative URLs, e.g. <img src="/images/a.png"> and must not be empty
3384      * will look
for an image file in $basedir/images/a.png and convert it to inline.
3385      * If you don
't provide a $basedir, relative paths will be left untouched (and thus probably break in email)
3386      * If you don
't want to apply these transformations to your HTML, just set Body and AltBody directly.
3387      * @access
public
3388      * @param
string $message HTML message string
3389      * @param
string $basedir Absolute path to a base directory to prepend to relative paths to images
3390      * @param boolean|callable $advanced Whether to use the
internal HTML to text converter
3391      * or your own custom converter @see PHPMailer::html2text()
3392      * @
return string $message The transformed message Body
3393      */

3394     
public function msgHTML($message, $basedir = '', $advanced = false)
3395     {
3396         preg_match_all(
'/(src|background)=["\'](.*)["\']/Ui', $message, $images);
3397         
if (array_key_exists(2, $images)) {
3398             
if (strlen($basedir) > 1 && substr($basedir, -1) != '/') {
3399                 
// Ensure $basedir has a trailing /
3400                 $basedir .=
'/';
3401             }
3402             
foreach ($images[2] as $imgindex => $url) {
3403                 
// Convert data URIs into embedded images
3404                 
if (preg_match('#^data:(image[^;,]*)(;base64)?,#', $url, $match)) {
3405                     $data = substr($url, strpos($url,
','));
3406                     
if ($match[2]) {
3407                         $data = base64_decode($data);
3408                     }
else {
3409                         $data = rawurldecode($data);
3410                     }
3411                     $cid = md5($url) .
'@phpmailer.0'; // RFC2392 S 2
3412                     
if ($this->addStringEmbeddedImage($data, $cid, 'embed' . $imgindex, 'base64', $match[1])) {
3413                         $message = str_replace(
3414                             $images[
0][$imgindex],
3415                             $images[
1][$imgindex] . '="cid:' . $cid . '"',
3416                             $message
3417                         );
3418                     }
3419                     
continue;
3420                 }
3421                 
if (
3422                     
// Only process relative URLs if a basedir is provided (i.e. no absolute local paths)
3423                     !empty($basedir)
3424                     
// Ignore URLs containing parent dir traversal (..)
3425                     && (strpos($url,
'..') === false)
3426                     
// Do not change urls that are already inline images
3427                     && substr($url,
0, 4) !== 'cid:'
3428                     
// Do not change absolute URLs, including anonymous protocol
3429                     && !preg_match(
'#^[a-z][a-z0-9+.-]*:?//#i', $url)
3430                 ) {
3431                     $filename = basename($url);
3432                     $directory = dirname($url);
3433                     
if ($directory == '.') {
3434                         $directory =
'';
3435                     }
3436                     $cid = md5($url) .
'@phpmailer.0'; // RFC2392 S 2
3437                     
if (strlen($directory) > 1 && substr($directory, -1) != '/') {
3438                         $directory .=
'/';
3439                     }
3440                     
if ($this->addEmbeddedImage(
3441                         $basedir . $directory . $filename,
3442                         $cid,
3443                         $filename,
3444                         
'base64',
3445                         self::_mime_types((
string)self::mb_pathinfo($filename, PATHINFO_EXTENSION))
3446                     )
3447                     ) {
3448                         $message = preg_replace(
3449                             
'/' . $images[1][$imgindex] . '=["\']' . preg_quote($url, '/') . '["\']/Ui',
3450                             $images[
1][$imgindex] . '="cid:' . $cid . '"',
3451                             $message
3452                         );
3453                     }
3454                 }
3455             }
3456         }
3457         $
this->isHTML(true);
3458         
// Convert all message body line breaks to CRLF, makes quoted-printable encoding work much better
3459         $
this->Body = $this->normalizeBreaks($message);
3460         $
this->AltBody = $this->normalizeBreaks($this->html2text($message, $advanced));
3461         
if (!$this->alternativeExists()) {
3462             $
this->AltBody = 'To view this email message, open it in a program that understands HTML!' .
3463                 self::CRLF . self::CRLF;
3464         }
3465         
return $this->Body;
3466     }
3467
3468     
/**
3469      * Convert an HTML
string into plain text.
3470      * This
is used by msgHTML().
3471      * Note - older versions of
this function used a bundled advanced converter
3472      * which was been removed
for license reasons in #232.
3473      * Example usage:
3474      * <code>
3475      * // Use
default conversion
3476      * $plain = $mail->html2text($html);
3477      * // Use your own custom converter
3478      * $plain = $mail->html2text($html, function($html) {
3479      * $converter =
new MyHtml2text($html);
3480      *
return $converter->get_text();
3481      * });
3482      * </code>
3483      * @param
string $html The HTML text to convert
3484      * @param boolean|callable $advanced Any boolean
value to use the internal converter,
3485      * or provide your own callable
for custom conversion.
3486      * @
return string
3487      */

3488     
public function html2text($html, $advanced = false)
3489     {
3490         
if (is_callable($advanced)) {
3491             
return call_user_func($advanced, $html);
3492         }
3493         
return html_entity_decode(
3494             trim(strip_tags(preg_replace(
'/<(head|title|style|script)[^>]*>.*?<\/\\1>/si', '', $html))),
3495             ENT_QUOTES,
3496             $
this->CharSet
3497         );
3498     }
3499
3500     
/**
3501      * Get the MIME type
for a file extension.
3502      * @param
string $ext File extension
3503      * @access
public
3504      * @
return string MIME type of file.
3505      * @
static
3506      */

3507     
public static function _mime_types($ext = '')
3508     {
3509         $mimes = array(
3510             
'xl' => 'application/excel',
3511             
'js' => 'application/javascript',
3512             
'hqx' => 'application/mac-binhex40',
3513             
'cpt' => 'application/mac-compactpro',
3514             
'bin' => 'application/macbinary',
3515             
'doc' => 'application/msword',
3516             
'word' => 'application/msword',
3517             
'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
3518             
'xltx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template',
3519             
'potx' => 'application/vnd.openxmlformats-officedocument.presentationml.template',
3520             
'ppsx' => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow',
3521             
'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
3522             
'sldx' => 'application/vnd.openxmlformats-officedocument.presentationml.slide',
3523             
'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
3524             
'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template',
3525             
'xlam' => 'application/vnd.ms-excel.addin.macroEnabled.12',
3526             
'xlsb' => 'application/vnd.ms-excel.sheet.binary.macroEnabled.12',
3527             
'class' => 'application/octet-stream',
3528             
'dll' => 'application/octet-stream',
3529             
'dms' => 'application/octet-stream',
3530             
'exe' => 'application/octet-stream',
3531             
'lha' => 'application/octet-stream',
3532             
'lzh' => 'application/octet-stream',
3533             
'psd' => 'application/octet-stream',
3534             
'sea' => 'application/octet-stream',
3535             
'so' => 'application/octet-stream',
3536             
'oda' => 'application/oda',
3537             
'pdf' => 'application/pdf',
3538             
'ai' => 'application/postscript',
3539             
'eps' => 'application/postscript',
3540             
'ps' => 'application/postscript',
3541             
'smi' => 'application/smil',
3542             
'smil' => 'application/smil',
3543             
'mif' => 'application/vnd.mif',
3544             
'xls' => 'application/vnd.ms-excel',
3545             
'ppt' => 'application/vnd.ms-powerpoint',
3546             
'wbxml' => 'application/vnd.wap.wbxml',
3547             
'wmlc' => 'application/vnd.wap.wmlc',
3548             
'dcr' => 'application/x-director',
3549             
'dir' => 'application/x-director',
3550             
'dxr' => 'application/x-director',
3551             
'dvi' => 'application/x-dvi',
3552             
'gtar' => 'application/x-gtar',
3553             
'php3' => 'application/x-httpd-php',
3554             
'php4' => 'application/x-httpd-php',
3555             
'php' => 'application/x-httpd-php',
3556             
'phtml' => 'application/x-httpd-php',
3557             
'phps' => 'application/x-httpd-php-source',
3558             
'swf' => 'application/x-shockwave-flash',
3559             
'sit' => 'application/x-stuffit',
3560             
'tar' => 'application/x-tar',
3561             
'tgz' => 'application/x-tar',
3562             
'xht' => 'application/xhtml+xml',
3563             
'xhtml' => 'application/xhtml+xml',
3564             
'zip' => 'application/zip',
3565             
'mid' => 'audio/midi',
3566             
'midi' => 'audio/midi',
3567             
'mp2' => 'audio/mpeg',
3568             
'mp3' => 'audio/mpeg',
3569             
'mpga' => 'audio/mpeg',
3570             
'aif' => 'audio/x-aiff',
3571             
'aifc' => 'audio/x-aiff',
3572             
'aiff' => 'audio/x-aiff',
3573             
'ram' => 'audio/x-pn-realaudio',
3574             
'rm' => 'audio/x-pn-realaudio',
3575             
'rpm' => 'audio/x-pn-realaudio-plugin',
3576             
'ra' => 'audio/x-realaudio',
3577             
'wav' => 'audio/x-wav',
3578             
'bmp' => 'image/bmp',
3579             
'gif' => 'image/gif',
3580             
'jpeg' => 'image/jpeg',
3581             
'jpe' => 'image/jpeg',
3582             
'jpg' => 'image/jpeg',
3583             
'png' => 'image/png',
3584             
'tiff' => 'image/tiff',
3585             
'tif' => 'image/tiff',
3586             
'eml' => 'message/rfc822',
3587             
'css' => 'text/css',
3588             
'html' => 'text/html',
3589             
'htm' => 'text/html',
3590             
'shtml' => 'text/html',
3591             
'log' => 'text/plain',
3592             
'text' => 'text/plain',
3593             
'txt' => 'text/plain',
3594             
'rtx' => 'text/richtext',
3595             
'rtf' => 'text/rtf',
3596             
'vcf' => 'text/vcard',
3597             
'vcard' => 'text/vcard',
3598             
'xml' => 'text/xml',
3599             
'xsl' => 'text/xml',
3600             
'mpeg' => 'video/mpeg',
3601             
'mpe' => 'video/mpeg',
3602             
'mpg' => 'video/mpeg',
3603             
'mov' => 'video/quicktime',
3604             
'qt' => 'video/quicktime',
3605             
'rv' => 'video/vnd.rn-realvideo',
3606             
'avi' => 'video/x-msvideo',
3607             
'movie' => 'video/x-sgi-movie'
3608         );
3609         
if (array_key_exists(strtolower($ext), $mimes)) {
3610             
return $mimes[strtolower($ext)];
3611         }
3612         
return 'application/octet-stream';
3613     }
3614
3615     
/**
3616      * Map a file name to a MIME type.
3617      * Defaults to
'application/octet-stream', i.e.. arbitrary binary data.
3618      * @param
string $filename A file name or full path, does not need to exist as a file
3619      * @
return string
3620      * @
static
3621      */

3622     
public static function filenameToType($filename)
3623     {
3624         
// In case the path is a URL, strip any query string before getting extension
3625         $qpos = strpos($filename,
'?');
3626         
if (false !== $qpos) {
3627             $filename = substr($filename,
0, $qpos);
3628         }
3629         $pathinfo = self::mb_pathinfo($filename);
3630         
return self::_mime_types($pathinfo['extension']);
3631     }
3632
3633     
/**
3634      * Multi-
byte-safe pathinfo replacement.
3635      * Drop-
in replacement for pathinfo(), but multibyte-safe, cross-platform-safe, old-version-safe.
3636      * Works similarly to the one
in PHP >= 5.2.0
3637      * @link http://www.php.net/manual/en/function.pathinfo.php#
107461
3638      * @param
string $path A filename or path, does not need to exist as a file
3639      * @param integer|
string $options Either a PATHINFO_* constant,
3640      * or a
string name to return only the specified piece, allows 'filename' to work on PHP < 5.2
3641      * @
return string|array
3642      * @
static
3643      */

3644     
public static function mb_pathinfo($path, $options = null)
3645     {
3646         $ret = array(
'dirname' => '', 'basename' => '', 'extension' => '', 'filename' => '');
3647         $pathinfo = array();
3648         
if (preg_match('%^(.*?)[\\\\/]*(([^/\\\\]*?)(\.([^\.\\\\/]+?)|))[\\\\/\.]*$%im', $path, $pathinfo)) {
3649             
if (array_key_exists(1, $pathinfo)) {
3650                 $ret[
'dirname'] = $pathinfo[1];
3651             }
3652             
if (array_key_exists(2, $pathinfo)) {
3653                 $ret[
'basename'] = $pathinfo[2];
3654             }
3655             
if (array_key_exists(5, $pathinfo)) {
3656                 $ret[
'extension'] = $pathinfo[5];
3657             }
3658             
if (array_key_exists(3, $pathinfo)) {
3659                 $ret[
'filename'] = $pathinfo[3];
3660             }
3661         }
3662         
switch ($options) {
3663             
case PATHINFO_DIRNAME:
3664             
case 'dirname':
3665                 
return $ret['dirname'];
3666             
case PATHINFO_BASENAME:
3667             
case 'basename':
3668                 
return $ret['basename'];
3669             
case PATHINFO_EXTENSION:
3670             
case 'extension':
3671                 
return $ret['extension'];
3672             
case PATHINFO_FILENAME:
3673             
case 'filename':
3674                 
return $ret['filename'];
3675             
default:
3676                 
return $ret;
3677         }
3678     }
3679
3680     
/**
3681      * Set or reset instance properties.
3682      * You should avoid
this function - it's more verbose, less efficient, more error-prone and
3683      * harder to debug than setting properties directly.
3684      * Usage Example:
3685      * `$mail->
set('SMTPSecure', 'tls');`
3686      *
is the same as:
3687      * `$mail->SMTPSecure =
'tls';`
3688      * @access
public
3689      * @param
string $name The property name to set
3690      * @param mixed $
value The value to set the property to
3691      * @
return boolean
3692      * @TODO Should
this not be using the __set() magic function?
3693      */

3694     
public function set($name, $value = '')
3695     {
3696         
if (property_exists($this, $name)) {
3697             $
this->$name = $value;
3698             
return true;
3699         }
else {
3700             $
this->setError($this->lang('variable_set') . $name);
3701             
return false;
3702         }
3703     }
3704
3705     
/**
3706      * Strip newlines to prevent header injection.
3707      * @access
public
3708      * @param
string $str
3709      * @
return string
3710      */

3711     
public function secureHeader($str)
3712     {
3713         
return trim(str_replace(array("\r", "\n"), '', $str));
3714     }
3715
3716     
/**
3717      * Normalize line breaks
in a string.
3718      * Converts UNIX LF, Mac CR and Windows CRLF line breaks
into a single line break format.
3719      * Defaults to CRLF (
for message bodies) and preserves consecutive breaks.
3720      * @param
string $text
3721      * @param
string $breaktype What kind of line break to use, defaults to CRLF
3722      * @
return string
3723      * @access
public
3724      * @
static
3725      */

3726     
public static function normalizeBreaks($text, $breaktype = "\r\n")
3727     {
3728         
return preg_replace('/(\r\n|\r|\n)/ms', $breaktype, $text);
3729     }
3730
3731     
/**
3732      * Set the
public and private key files and password for S/MIME signing.
3733      * @access
public
3734      * @param
string $cert_filename
3735      * @param
string $key_filename
3736      * @param
string $key_pass Password for private key
3737      * @param
string $extracerts_filename Optional path to chain certificate
3738      */

3739     
public function sign($cert_filename, $key_filename, $key_pass, $extracerts_filename = '')
3740     {
3741         $
this->sign_cert_file = $cert_filename;
3742         $
this->sign_key_file = $key_filename;
3743         $
this->sign_key_pass = $key_pass;
3744         $
this->sign_extracerts_file = $extracerts_filename;
3745     }
3746
3747     
/**
3748      * Quoted-Printable-encode a DKIM header.
3749      * @access
public
3750      * @param
string $txt
3751      * @
return string
3752      */

3753     
public function DKIM_QP($txt)
3754     {
3755         $line =
'';
3756         
for ($i = 0; $i < strlen($txt); $i++) {
3757             $ord = ord($txt[$i]);
3758             
if (((0x21 <= $ord) && ($ord <= 0x3A)) || $ord == 0x3C || ((0x3E <= $ord) && ($ord <= 0x7E))) {
3759                 $line .= $txt[$i];
3760             }
else {
3761                 $line .=
'=' . sprintf('%02X', $ord);
3762             }
3763         }
3764         
return $line;
3765     }
3766
3767     
/**
3768      * Generate a DKIM signature.
3769      * @access
public
3770      * @param
string $signHeader
3771      * @throws phpmailerException
3772      * @
return string The DKIM signature value
3773      */

3774     
public function DKIM_Sign($signHeader)
3775     {
3776         
if (!defined('PKCS7_TEXT')) {
3777             
if ($this->exceptions) {
3778                 
throw new phpmailerException($this->lang('extension_missing') . 'openssl');
3779             }
3780             
return '';
3781         }
3782         $privKeyStr = !empty($
this->DKIM_private_string) ? $this->DKIM_private_string : file_get_contents($this->DKIM_private);
3783         
if ('' != $this->DKIM_passphrase) {
3784             $privKey = openssl_pkey_get_private($privKeyStr, $
this->DKIM_passphrase);
3785         }
else {
3786             $privKey = openssl_pkey_get_private($privKeyStr);
3787         }
3788         
//Workaround for missing digest algorithms in old PHP & OpenSSL versions
3789         
//@link http://stackoverflow.com/a/11117338/333340
3790         
if (version_compare(PHP_VERSION, '5.3.0') >= 0 and
3791             in_array(
'sha256WithRSAEncryption', openssl_get_md_methods(true))) {
3792             
if (openssl_sign($signHeader, $signature, $privKey, 'sha256WithRSAEncryption')) {
3793                 openssl_pkey_free($privKey);
3794                 
return base64_encode($signature);
3795             }
3796         }
else {
3797             $pinfo = openssl_pkey_get_details($privKey);
3798             $hash = hash(
'sha256', $signHeader);
3799             
//'Magic' constant for SHA256 from RFC3447
3800             
//@link https://tools.ietf.org/html/rfc3447#page-43
3801             $t =
'3031300d060960864801650304020105000420' . $hash;
3802             $pslen = $pinfo[
'bits'] / 8 - (strlen($t) / 2 + 3);
3803             $eb = pack(
'H*', '0001' . str_repeat('FF', $pslen) . '00' . $t);
3804
3805             
if (openssl_private_encrypt($eb, $signature, $privKey, OPENSSL_NO_PADDING)) {
3806                 openssl_pkey_free($privKey);
3807                 
return base64_encode($signature);
3808             }
3809         }
3810         openssl_pkey_free($privKey);
3811         
return '';
3812     }
3813
3814     
/**
3815      * Generate a DKIM canonicalization header.
3816      * @access
public
3817      * @param
string $signHeader Header
3818      * @
return string
3819      */

3820     
public function DKIM_HeaderC($signHeader)
3821     {
3822         $signHeader = preg_replace(
'/\r\n\s+/', ' ', $signHeader);
3823         $lines = explode(
"\r\n", $signHeader);
3824         
foreach ($lines as $key => $line) {
3825             list($heading, $
value) = explode(':', $line, 2);
3826             $heading = strtolower($heading);
3827             $
value = preg_replace('/\s{2,}/', ' ', $value); // Compress useless spaces
3828             $lines[$key] = $heading .
':' . trim($value); // Don't forget to remove WSP around the value
3829         }
3830         $signHeader = implode(
"\r\n", $lines);
3831         
return $signHeader;
3832     }
3833
3834     
/**
3835      * Generate a DKIM canonicalization body.
3836      * @access
public
3837      * @param
string $body Message Body
3838      * @
return string
3839      */

3840     
public function DKIM_BodyC($body)
3841     {
3842         
if ($body == '') {
3843             
return "\r\n";
3844         }
3845         
// stabilize line endings
3846         $body = str_replace(
"\r\n", "\n", $body);
3847         $body = str_replace(
"\n", "\r\n", $body);
3848         
// END stabilize line endings
3849         
while (substr($body, strlen($body) - 4, 4) == "\r\n\r\n") {
3850             $body = substr($body,
0, strlen($body) - 2);
3851         }
3852         
return $body;
3853     }
3854
3855     
/**
3856      * Create the DKIM header and body
in a new message header.
3857      * @access
public
3858      * @param
string $headers_line Header lines
3859      * @param
string $subject Subject
3860      * @param
string $body Body
3861      * @
return string
3862      */

3863     
public function DKIM_Add($headers_line, $subject, $body)
3864     {
3865         $DKIMsignatureType =
'rsa-sha256'; // Signature & hash algorithms
3866         $DKIMcanonicalization =
'relaxed/simple'; // Canonicalization of header/body
3867         $DKIMquery =
'dns/txt'; // Query method
3868         $DKIMtime = time();
// Signature Timestamp = seconds since 00:00:00 - Jan 1, 1970 (UTC time zone)
3869         $subject_header =
"Subject: $subject";
3870         $headers = explode($
this->LE, $headers_line);
3871         $from_header =
'';
3872         $to_header =
'';
3873         $date_header =
'';
3874         $current =
'';
3875         
foreach ($headers as $header) {
3876             
if (strpos($header, 'From:') === 0) {
3877                 $from_header = $header;
3878                 $current =
'from_header';
3879             } elseif (strpos($header,
'To:') === 0) {
3880                 $to_header = $header;
3881                 $current =
'to_header';
3882             } elseif (strpos($header,
'Date:') === 0) {
3883                 $date_header = $header;
3884                 $current =
'date_header';
3885             }
else {
3886                 
if (!empty($$current) && strpos($header, ' =?') === 0) {
3887                     $$current .= $header;
3888                 }
else {
3889                     $current =
'';
3890                 }
3891             }
3892         }
3893         $
from = str_replace('|', '=7C', $this->DKIM_QP($from_header));
3894         $to = str_replace(
'|', '=7C', $this->DKIM_QP($to_header));
3895         $date = str_replace(
'|', '=7C', $this->DKIM_QP($date_header));
3896         $subject = str_replace(
3897             
'|',
3898             
'=7C',
3899             $
this->DKIM_QP($subject_header)
3900         );
// Copied header fields (dkim-quoted-printable)
3901         $body = $
this->DKIM_BodyC($body);
3902         $DKIMlen = strlen($body);
// Length of body
3903         $DKIMb64 = base64_encode(pack(
'H*', hash('sha256', $body))); // Base64 of packed binary SHA-256 hash of body
3904         
if ('' == $this->DKIM_identity) {
3905             $ident =
'';
3906         }
else {
3907             $ident =
' i=' . $this->DKIM_identity . ';';
3908         }
3909         $dkimhdrs =
'DKIM-Signature: v=1; a=' .
3910             $DKIMsignatureType .
'; q=' .
3911             $DKIMquery .
'; l=' .
3912             $DKIMlen .
'; s=' .
3913             $
this->DKIM_selector .
3914             
";\r\n" .
3915             
"\tt=" . $DKIMtime . '; c=' . $DKIMcanonicalization . ";\r\n" .
3916             
"\th=From:To:Date:Subject;\r\n" .
3917             
"\td=" . $this->DKIM_domain . ';' . $ident . "\r\n" .
3918             
"\tz=$from\r\n" .
3919             
"\t|$to\r\n" .
3920             
"\t|$date\r\n" .
3921             
"\t|$subject;\r\n" .
3922             
"\tbh=" . $DKIMb64 . ";\r\n" .
3923             
"\tb=";
3924         $toSign = $
this->DKIM_HeaderC(
3925             $from_header .
"\r\n" .
3926             $to_header .
"\r\n" .
3927             $date_header .
"\r\n" .
3928             $subject_header .
"\r\n" .
3929             $dkimhdrs
3930         );
3931         $signed = $
this->DKIM_Sign($toSign);
3932         
return $dkimhdrs . $signed . "\r\n";
3933     }
3934
3935     
/**
3936      * Detect
if a string contains a line longer than the maximum line length allowed.
3937      * @param
string $str
3938      * @
return boolean
3939      * @
static
3940      */

3941     
public static function hasLineLongerThanMax($str)
3942     {
3943         
//+2 to include CRLF line break for a 1000 total
3944         
return (boolean)preg_match('/^(.{'.(self::MAX_LINE_LENGTH + 2).',})/m', $str);
3945     }
3946
3947     
/**
3948      * Allows
for public read access to 'to' property.
3949      * @note: Before the send() call, queued addresses (i.e. with IDN) are not yet included.
3950      * @access
public
3951      * @
return array
3952      */

3953     
public function getToAddresses()
3954     {
3955         
return $this->to;
3956     }
3957
3958     
/**
3959      * Allows
for public read access to 'cc' property.
3960      * @note: Before the send() call, queued addresses (i.e. with IDN) are not yet included.
3961      * @access
public
3962      * @
return array
3963      */

3964     
public function getCcAddresses()
3965     {
3966         
return $this->cc;
3967     }
3968
3969     
/**
3970      * Allows
for public read access to 'bcc' property.
3971      * @note: Before the send() call, queued addresses (i.e. with IDN) are not yet included.
3972      * @access
public
3973      * @
return array
3974      */

3975     
public function getBccAddresses()
3976     {
3977         
return $this->bcc;
3978     }
3979
3980     
/**
3981      * Allows
for public read access to 'ReplyTo' property.
3982      * @note: Before the send() call, queued addresses (i.e. with IDN) are not yet included.
3983      * @access
public
3984      * @
return array
3985      */

3986     
public function getReplyToAddresses()
3987     {
3988         
return $this->ReplyTo;
3989     }
3990
3991     
/**
3992      * Allows
for public read access to 'all_recipients' property.
3993      * @note: Before the send() call, queued addresses (i.e. with IDN) are not yet included.
3994      * @access
public
3995      * @
return array
3996      */

3997     
public function getAllRecipientAddresses()
3998     {
3999         
return $this->all_recipients;
4000     }
4001
4002     
/**
4003      * Perform a callback.
4004      * @param boolean $isSent
4005      * @param array $to
4006      * @param array $cc
4007      * @param array $bcc
4008      * @param
string $subject
4009      * @param
string $body
4010      * @param
string $from
4011      */

4012     
protected function doCallback($isSent, $to, $cc, $bcc, $subject, $body, $from)
4013     {
4014         
if (!empty($this->action_function) && is_callable($this->action_function)) {
4015             $
params = array($isSent, $to, $cc, $bcc, $subject, $body, $from);
4016             call_user_func_array($
this->action_function, $params);
4017         }
4018     }
4019 }

4020
4021 /**
4022  * PHPMailer exception handler
4023  * @package PHPMailer
4024  */

4025 class
phpmailerException extends Exception
4026 {
4027     
/**
4028      * Prettify error message output
4029      * @
return string
4030      */

4031     
public function errorMessage()
4032     {
4033         $errorMsg =
'<strong>' . $this->getMessage() . "</strong><br />\n";
4034         
return $errorMsg;
4035     }
4036 }


Gõ tìm kiếm nhanh...